将对象文字分配给打字稿通用类型
Assigning an object literal to a typescript generic type
我正在构建一个 FormConditions
接口,其中我将有一个具有任意键的对象,每个键都是实现 Condition
接口的 class 的一个实例。我想将此对象文字分配给一个变量,并使结果对象的类型 A) 仅响应对象文字中的键,并且 B) 尊重任何 subclassing 或其他扩展钥匙可能有。
如果您检查下面的代码,我发现所有实际类型都运行良好。我遇到的问题 运行 是我不知道如何在不显式声明每个键的子类型的情况下将对象直接分配给变量。相反,我可以通过身份函数 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
简短回答:不,你不能分配包含异构类型的对象文字和维护泛型类型约束。需要一个受约束的辅助函数(如当前实现的那样)。
扩展接口 Condition
以包括所有子类型属性
可以扩展 Condition
的定义以接受可选的 entries
数组,这样 FormConditions<E, T extends Record<string, Condition<E>>>
就可以同时包含 SimpleConditions
和 ListConditions
。这会产生不良副作用,即 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
仅包含 name
和 id
由于 Condition
受限,因此在尝试访问 SimpleCondition
实例上的 entries
时出现错误(正如预期的那样)。但是,在 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)
我正在构建一个 FormConditions
接口,其中我将有一个具有任意键的对象,每个键都是实现 Condition
接口的 class 的一个实例。我想将此对象文字分配给一个变量,并使结果对象的类型 A) 仅响应对象文字中的键,并且 B) 尊重任何 subclassing 或其他扩展钥匙可能有。
如果您检查下面的代码,我发现所有实际类型都运行良好。我遇到的问题 运行 是我不知道如何在不显式声明每个键的子类型的情况下将对象直接分配给变量。相反,我可以通过身份函数 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
简短回答:不,你不能分配包含异构类型的对象文字和维护泛型类型约束。需要一个受约束的辅助函数(如当前实现的那样)。
扩展接口 Condition
以包括所有子类型属性
可以扩展 Condition
的定义以接受可选的 entries
数组,这样 FormConditions<E, T extends Record<string, Condition<E>>>
就可以同时包含 SimpleConditions
和 ListConditions
。这会产生不良副作用,即 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
仅包含 name
和 id
由于 Condition
受限,因此在尝试访问 SimpleCondition
实例上的 entries
时出现错误(正如预期的那样)。但是,在 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)