简体   繁体   English

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

[英]Assigning an object literal to a typescript generic type

I am building a FormConditions interface where I will have an object with arbitrary keys, each of which are an instance of a class implementing a Condition interface.我正在构建一个FormConditions接口,其中我将拥有一个带有任意键的对象,每个键都是实现Condition接口的类的实例。 I want to assign this object literal to a variable, and have the type of the resulting object A) only respond to the keys in the object literal, and B) respect any subclassing or other extending that each of those keys may have.我想将此对象文字分配给一个变量,并使结果对象的类型 A) 仅响应对象文字中的键,并且 B) 尊重每个这些键可能具有的任何子类或其他扩展。

If you examine the code below, I have all of the actual types working just fine.如果您检查下面的代码,我会发现所有实际类型都可以正常工作。 The problem I'm running into is that I don't know how to assign the object to the variable directly without explicitly declaring the subtype of each key.我遇到的问题是我不知道如何将对象直接分配给变量而不显式声明每个键的子类型。 Instead I can pass it through the identity function makeFormConditions which uses generics to correctly infer the type of the resulting object.相反,我可以通过标识函数makeFormConditions传递它,该函数使用泛型来正确推断结果对象的类型。 Is this the only way to do this or is there a way to assign it directly?这是唯一的方法还是有办法直接分配它? Feel free to alter the definition of FormCondition as you see fit to accomplish this.您可以随意更改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

Here's a typescript playground link to the above.这是上面的打字稿游乐场链接

Short answer: No, you cannot assign an object literal containing heterogeneous types and maintain generic type constraints.简短回答:不,您不能分配包含异构类型的对象文字维护泛型类型约束。 A constrained helper function (as currently implemented) is required.需要一个受约束的辅助函数(如当前实现)。

Expanding Interface Condition to include all Sub-type Properties扩展接口Condition以包括所有子类型属性

The definition of Condition could be expanded to accept an optional entries array, such that FormConditions<E, T extends Record<string, Condition<E>>> could hold both SimpleConditions and ListConditions .可以扩展Condition的定义以接受可选的entries数组,例如FormConditions<E, T extends Record<string, Condition<E>>>可以同时包含SimpleConditionsListConditions This has the undesired side-effect that instances of SimpleCondition may reference the missing entries property without error.这会产生不希望的副作用,即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.

Limit Interface Condition to include only name and id将接口Condition限制为仅包含nameid

Since Condition is limited, there is an error (as expected) when attempting to access entries on instances of SimpleCondition .由于Condition是有限的,因此在尝试访问SimpleCondition实例上的entries时会出现错误(正如预期的那样)。 However, in the context of type FormConditions<E, T extends Record<string, Condition>> , instances of ListCondition result in an error when referring to entries , since the type has been narrowed to Condition .然而,在上下文type FormConditions<E, T extends Record<string, Condition>> ,实例ListCondition参考时将导致错误entries ,由于类型已缩小到Condition

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么 TypeScript 在将 object 文字分配给扩展 object 文字的泛型类型时会抱怨? - Why TypeScript complains when assigning an object literal to a generic type extending object literals? 打字稿不能将对象文字分配给泛型类型 - Typescript cannot assign object literal to a generic type 在 Typescript 中动态分配给 object 文字 - Assigning to an object literal dynamically in Typescript 打字稿将泛型约束为字符串文字类型以用于计算对象属性 - Typescript constrain generic to string literal type for use in computed object property Typescript 类型为 object 字面量 - Typescript type to object literal 为什么在对象文字中赋值时 TypeScript 将字符串文字联合类型转换为字符串? - why is TypeScript converting string literal union type to string when assigning in object literal? 打字稿:一般类型被推断为文字类型 - Typescript: Generic Type being inferred to Literal Type TypeScript泛型:泛型函数类型和对象文字类型的调用签名不相等(类型&#39;T&#39;不能分配给类型&#39;T&#39;) - TypeScript generics: Inequivalence of generic function type and call signature of an object literal type (Type 'T' is not assignable to type 'T') 类型断言对象字面量打字稿 - Type assertion object literal typescript TypeScript 中对象字面量的类型定义 - Type definition in object literal in TypeScript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM