troy
troy

Reputation: 419

TypeScript: infer record union value

interface Num {
    type: 'number',
    default: number
}

interface Bool {
    type: 'boolean'
    default: boolean
}

interface Str {
    type: 'string',
    default: string
}

type UnionType = Num | Bool | Str

interface IOption<T extends string, CFG extends Record<T, UnionType>> {
    configs: CFG,
    // I don't know how to write this line to make each key matches its own value type
    // i.e. `{key1: Num, key2: Bool, key3: Str}`
    defaultOverwrite?: Partial<{ [key in T]: CFG[key]['default'] }>
}

declare function checker<T extends string, CFG extends Record<T, UnionType>>(option: IOption<T, CFG>): void

checker({
    configs: {
        // key1 errors as expected, which default should be a number
        key1: { type: 'number', default: '' },
        key2: { type: 'boolean', default: true },
        key3: { type: 'string', default: 'string' },
    },
    defaultOverwrite: {
        // the value of `key1` should match the type of configs.key1.default, which is a number.
        // expected error here, but I don't know how
        key1: '3'
    }
})

See the above case.

I want to check the configs type which value should matches UnionType, also, I want to check defaultOverwrite type, which should has partial key of configs and the corresponding value type as the configs declared.

But I don't know how, anyone can help me?

I rethought about my question, the real problem is we need to make a union value record to become exact type when assigned value, like

type A = {[key: string]: string | number}
// when declared
const a: A = {
  key1: 1,
  key2: 'str'
}

I want a to match type A but also generate a exact type which is

interface ExactA{
  key1: number,
  key2: string
}

Playground

Upvotes: 3

Views: 337

Answers (1)

Dimava
Dimava

Reputation: 10881

You can't expect more then ONE error in a generic

The error in defaultOverwrite will appear as soon as you fix the error in configs

checker({
    configs: {
        key1: { type: 'number', default: 1 }, // error fixed
        key2: { type: 'boolean', default: true },
        key3: { type: 'string', default: 'string' },
    },
    defaultOverwrite: {
        key1: '1' // a new error has spawned
            // (property) key1?: number | undefined
            //Type 'string' is not assignable to type 'number'.(2322)
    }
})

Playground

Upvotes: 1

Related Questions