[英]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.需要一个受约束的辅助函数(如当前实现)。
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>>>
可以同时包含SimpleConditions
和ListConditions
。 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.
Condition
to include only name
and id
将接口Condition
限制为仅包含name
和id
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.