如何键入强制对象值以匹配对象键?
How to type-enforce object value to match object key?
我有实现这样类型的对象:
type TMyObject<T extends string> {
category: T
}
我需要将它们静态存储在另一个对象中,并确保第二个对象的键与 category
字段的值匹配,如下所示:
const myObject: TMyObject<'foo'> = { category: 'foo' }
const dico = {
foo: myObject, // good
bar: myObject, // bad: 'bar' key does not match myObject.category
}
我遇到这种情况,因为我有接口扩展IMyObject
并将类别字段固定为精确值,像这样:
type TMyFooObject = IMyObject<'foo'>
我花了两个小时试图为 dico
对象创建一个可以按描述工作的类型,但我就是想不出解决这个问题的方法 ^^
重要说明:category
字段和扩展 TMyObject
的可能类型不是静态的,我们不能在这里使用“简单”联合...
一如既往,非常感谢您花时间阅读并回答这个问题!
只要 IMyObject
有 string
类型的 cateogory
这就是运行时检查,而不是编译时检查。
您可以将检查强制执行为每个案例的特定类型的编译时检查:
interface IFooObject {
category: 'foo';
foo: number;
//other foo props
}
interface IBarObject {
category: 'bar';
bar: number;
//other bar props
}
type IObject = IFooObject | IBarObject;
const myObject: IObject = { category: 'foo', foo: 123 }
type Disco<T> = T extends { category: infer U } ? U extends string ? { [P in U]: T } : never : never;
const disco: Disco<IObject> = {
foo: myObject, // good
bar: myObject, // this now fails!
};
你也可以使用泛型类型,但在那种情况下你需要像这样一直传递它:
type TMyObject<T extends string> = {
category: T
}
const myObject: TMyObject<'foo'> = { category: 'foo' }
type Disco<T> = T extends { category: infer U } ? U extends string ? { [P in U]: T } : never : never;
const dico: Disco<typeof myObject> = {
foo: myObject, // good
bar: myObject, // bad: 'bar' key does not match myObject.category
}
我们可以利用 mapped types 来根据对象的键定义对象的值。
type Dico<Keys extends string> = {
[K in Keys]: TMyObject<K>
}
如果我们事先知道所有类别,那么我们可以创建一个以这些类别为键的地图。
const dico: Dico<'foo' | 'bar'> = {
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
}
如果您想将其限制为一组有效类别但不要求所有类别都存在,则可以使用 Partial<Dico<Category>>
。
只有当您的类型设置为您已经可以轻松访问所有类别的联合类型时,这才有意义。
我们希望能够只查看对象并查看其键是否与其类别字符串匹配。我们可以做到这一点,但前提是我们通过恒等函数创建对象。泛型函数允许我们从对象推断 Keys
类型。
const makeDico = <Keys extends string>(dico: Dico<Keys>) => dico;
const dico = makeDico({
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
});
// dico has type: Dico<"foo" | "bar">
我有实现这样类型的对象:
type TMyObject<T extends string> {
category: T
}
我需要将它们静态存储在另一个对象中,并确保第二个对象的键与 category
字段的值匹配,如下所示:
const myObject: TMyObject<'foo'> = { category: 'foo' }
const dico = {
foo: myObject, // good
bar: myObject, // bad: 'bar' key does not match myObject.category
}
我遇到这种情况,因为我有接口扩展IMyObject
并将类别字段固定为精确值,像这样:
type TMyFooObject = IMyObject<'foo'>
我花了两个小时试图为 dico
对象创建一个可以按描述工作的类型,但我就是想不出解决这个问题的方法 ^^
重要说明:category
字段和扩展 TMyObject
的可能类型不是静态的,我们不能在这里使用“简单”联合...
一如既往,非常感谢您花时间阅读并回答这个问题!
只要 IMyObject
有 string
类型的 cateogory
这就是运行时检查,而不是编译时检查。
您可以将检查强制执行为每个案例的特定类型的编译时检查:
interface IFooObject {
category: 'foo';
foo: number;
//other foo props
}
interface IBarObject {
category: 'bar';
bar: number;
//other bar props
}
type IObject = IFooObject | IBarObject;
const myObject: IObject = { category: 'foo', foo: 123 }
type Disco<T> = T extends { category: infer U } ? U extends string ? { [P in U]: T } : never : never;
const disco: Disco<IObject> = {
foo: myObject, // good
bar: myObject, // this now fails!
};
你也可以使用泛型类型,但在那种情况下你需要像这样一直传递它:
type TMyObject<T extends string> = {
category: T
}
const myObject: TMyObject<'foo'> = { category: 'foo' }
type Disco<T> = T extends { category: infer U } ? U extends string ? { [P in U]: T } : never : never;
const dico: Disco<typeof myObject> = {
foo: myObject, // good
bar: myObject, // bad: 'bar' key does not match myObject.category
}
我们可以利用 mapped types 来根据对象的键定义对象的值。
type Dico<Keys extends string> = {
[K in Keys]: TMyObject<K>
}
如果我们事先知道所有类别,那么我们可以创建一个以这些类别为键的地图。
const dico: Dico<'foo' | 'bar'> = {
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
}
如果您想将其限制为一组有效类别但不要求所有类别都存在,则可以使用 Partial<Dico<Category>>
。
只有当您的类型设置为您已经可以轻松访问所有类别的联合类型时,这才有意义。
我们希望能够只查看对象并查看其键是否与其类别字符串匹配。我们可以做到这一点,但前提是我们通过恒等函数创建对象。泛型函数允许我们从对象推断 Keys
类型。
const makeDico = <Keys extends string>(dico: Dico<Keys>) => dico;
const dico = makeDico({
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
});
// dico has type: Dico<"foo" | "bar">