Teddy Bang
Teddy Bang

Reputation: 61

How to get key type in interface

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

Answers (1)

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

Playground

Similar question

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

Related Questions