Reputation: 61
Here is a problem...
titleBarContainer.tsx
function TitleBarContainer() {
const systemData = useSelector((state: RootState) => state.counter.systemData)
const dispatch = useDispatch()
const onChangeSystemData = useCallback(
(key: keyof SystemState, value: SystemStateType) => {
let tempSystemData = Object.assign({}, systemData)
tempSystemData[key] = value // It doesn't work, but not main problem.
dispatch(changeSystemData(tempSystemData))
},
[dispatch, systemData]
)
}
system.d.ts
interface SystemState {
title: TitleStateType
windowState: WindowStateType
language: LanguageStateType
mode: ModeStateType
}
type TitleStateType = string
type LanguageStateType = 'ko-KR' | 'en-US'
type WindowStateType = 'normal' | 'minimize' | 'maximize' | 'fullscreen'
type ModeStateType = 'admin' | 'user'
type SystemStateType = TitleStateType | WindowStateType | LanguageStateType | ModeStateType
In onChangeSystemData Function, I want to get an usual type.
For example, if 'key' is 'normal', I want 'value' type to be WindowStateType not SystemStateType.
I tried using generic type. The following is the code I tried.
titleBarContainer.tsx (previous version, doesn't work.)
function TitleBarContainer() {
const systemData = useSelector((state: RootState) => state.counter.systemData)
const dispatch = useDispatch()
const onChangeSystemData<SystemState> = (key, value) => {
let tempSystemData = Object.assign({}, systemData)
tempSystemData[key] = value
dispatch(changeSystemData(tempSystemData))
}
}
system.d.ts (previous version)
interface SystemState {
title: TitleStateType
windowState: WindowStateType
language: LanguageStateType
mode: ModeStateType
}
type TitleStateType = string
type LanguageStateType = 'ko-KR' | 'en-US'
type WindowStateType = 'normal' | 'minimize' | 'maximize' | 'fullscreen'
type ModeStateType = 'admin' | 'user'
type SystemStateType = TitleStateType | WindowStateType | LanguageStateType | ModeStateType
interface InternalState<T> { (key: keyof T, value: T[keyof T]): void }
It is too diffcult to me... Give me advises or any solution.
Upvotes: 1
Views: 141
Reputation: 33041
YOu just need to infer the key
:
interface SystemState {
title: TitleStateType
windowState: WindowStateType
language: LanguageStateType
mode: ModeStateType
}
type TitleStateType = string
type LanguageStateType = 'ko-KR' | 'en-US'
type WindowStateType = 'normal' | 'minimize' | 'maximize' | 'fullscreen'
type ModeStateType = 'admin' | 'user'
type SystemStateType = TitleStateType | WindowStateType | LanguageStateType | ModeStateType
type InternalState<T> = <Key extends keyof T>(key: Key, value: T[Key]) => void
const onChangeSystemData: InternalState<SystemState> = (key, value) => {}
onChangeSystemData('windowState','minimize') // ok
onChangeSystemData('windowState','minimize222') // expected error
Why
interface InternalState<T> { <Key extends keyof T>(key: keyof T, value: T[Key]): void
does not trigger an error?
Because interface InternalState<T>
defines function as a method. MEthods are bivariant in typescript, this is by design. It means that if method parameters are assignable to passed arguments and vice versa - it will compile. It is unsafe to use such syntax, so try to avoid it as much as possible.
Btw, you can find some interesting examples in my blog which is fully typescript dedicated
Upvotes: 1