将对象文字分配给打字稿泛型类型

Wad*_*ndy 5 typescript typescript-generics

我正在构建一个FormConditions接口,其中我将拥有一个带有任意键的对象,每个键都是实现Condition接口的类的实例。我想将此对象文字分配给一个变量,并使结果对象的类型 A) 仅响应对象文字中的键,并且 B) 尊重每个这些键可能具有的任何子类化或其他扩展。

如果您检查下面的代码,我会发现所有实际类型都可以正常工作。我遇到的问题是我不知道如何将对象直接分配给变量而不显式声明每个键的子类型。相反,我可以通过标识函数传递它,该函数makeFormConditions使用泛型来正确推断结果对象的类型。这是唯一的方法还是有办法直接分配它?随意更改FormCondition您认为合适的定义以实现此目的。

interface Condition {
    name: string
    id: number
}

type FormConditions<T extends Record<string, Condition>> = {
    [P in keyof T]: T[P]
}

class SimpleCondition implements Condition {
    constructor(public name: string, public id: number) {}
}

class ListCondition<T> implements Condition {
    constructor(public name: string, public id: number, public entries: T[]) {}
}

// This is a passthrough function just to make the types work
function makeFormConditions<T extends Record<string, Condition>>(obj: T): FormConditions<T>  {
    return obj;
}

// Would prefer to avoid the function call to make types work
const conditions = makeFormConditions({
    simpleOne: new SimpleCondition('simpleOne', 1),
    simpleTwo: new SimpleCondition('simpleTwo', 2),
    list: new ListCondition('list', 3, ['foo', 'bar'])
})


// This works but is redundantly verbose
// const conditions : FormConditions<{
//     simpleOne: SimpleCondition;
//     simpleTwo: SimpleCondition;
//     list: ListCondition<string>;
// }> = {
//     simpleOne: new SimpleCondition('simpleOne', 1),
//     simpleTwo: new SimpleCondition('simpleTwo', 2),
//     list: new ListCondition('list', 3, ['foo', 'bar'])
// }
//
// would instead prefer to not use the function or be
// really specific about the type declaration:
// const conditions : FormConditions = {
//     simpleOne: new SimpleCondition('simpleOne', 1),
//     simpleTwo: new SimpleCondition('simpleTwo', 2),
//     list: new ListCondition('list', 3, ['foo', 'bar'])
// }

conditions.list.name
conditions.list.entries
conditions.simpleOne.name
conditions.simpleOne.entries // error, as expected
Run Code Online (Sandbox Code Playgroud)

这是上面的打字稿游乐场链接

Chr*_*ert 4

简短的回答:不,您不能分配包含异构类型的对象文字维护泛型类型约束。需要一个受约束的辅助函数(当前实现的)。

扩展接口Condition以包含所有子类型属性

的定义Condition可以扩展为接受一个可选entries数组,这样就FormConditions<E, T extends Record<string, Condition<E>>>可以同时容纳SimpleConditionsListConditions。这会产生不希望有的副作用,即 的实例可能会毫无错误地SimpleCondition引用缺失的属性。entries

interface Condition<E> {
    name: string
    id: number
    entries?: E[]
}

type FormConditions<E, T extends Record<string, Condition<E>>> = {
    [P in keyof T]: T[P]
}

class SimpleCondition<E = never> implements Condition<E> {
    constructor(public name: string, public id: number) {}
}

class ListCondition<E> implements Condition<E> {
    constructor(public name: string, public id: number, public entries: E[]) {}
}

const conditions: FormConditions<string, Record<string, Condition<string>>> = {
    simpleOne: new SimpleCondition('simpleOne', 1),
    simpleTwo: new SimpleCondition('simpleTwo', 2),
    list: new ListCondition('list', 3, ['foo', 'bar'])
}

conditions.list.name;
conditions.list.entries;
conditions.simpleOne.name;
conditions.simpleOne.entries;  // Expected error; however, no error, since `entries` is optional parameter.
Run Code Online (Sandbox Code Playgroud)

限制接口Condition仅包含nameid

由于受到限制,因此在尝试访问的实例Condition时会出现错误(如预期)。然而,在 的上下文中, 的实例在引用 时会导致错误,因为类型已缩小为。entriesSimpleConditiontype FormConditions<E, T extends Record<string, Condition>>ListConditionentriesCondition

interface Condition {
    name: string
    id: number
}

type FormConditions<E, T extends Record<string, Condition>> = {
    [P in keyof T]: T[P]
}

class SimpleCondition<E = never> implements Condition {
    constructor(public name: string, public id: number) {}
}

class ListCondition<E> implements Condition {
    constructor(public name: string, public id: number, public entries: E[]) {}
}

const conditions: FormConditions<string, Record<string, Condition>> = {
    simpleOne: new SimpleCondition('simpleOne', 1),
    simpleTwo: new SimpleCondition('simpleTwo', 2),
    list: new ListCondition('list', 3, ['foo', 'bar'])
}

conditions.list.name;
conditions.list.entries; // Error: Property 'entries' does not exist on type 'Condition'.
conditions.simpleOne.name;
conditions.simpleOne.entries; // error (as expected - Good)
Run Code Online (Sandbox Code Playgroud)