简体   繁体   English

如何强制执行 object 值以匹配 object 键?

[英]How to type-enforce object value to match object key?

I have objects that implements types like this:我有实现如下类型的对象:

type TMyObject<T extends string> {
  category: T
}

I need to statically store them in an other object, and ensure that the key of this second object matches the value of the category field, like so:我需要将它们静态存储在另一个 object 中,并确保第二个 object 的键与category字段的值匹配,如下所示:

const myObject: TMyObject<'foo'> = { category: 'foo' }

const dico = {
  foo: myObject, // good
  bar: myObject, // bad: 'bar' key does not match myObject.category
}

I encounter this case, because I have interfaces that extends IMyObject and fixes the category field to a precise value, like so:我遇到这种情况,因为我有扩展IMyObject并将类别字段固定为精确值的接口,如下所示:

type TMyFooObject = IMyObject<'foo'>

I spent two hours trying to create a type for the dico object that would work as described, but I just cannot figure a way to solve this ^^我花了两个小时试图为dico object 创建一个可以按描述工作的类型,但我就是想不出解决这个问题的方法^^

Important note: The category field and the possibles types extending TMyObject are not static, we cannot use a "simple" union here...重要提示: category字段和扩展TMyObject的可能类型不是 static,我们不能在这里使用“简单”联合...

As ever, a huge thanks for the time spent reading, and maybe answering to this question !与以往一样,非常感谢您花时间阅读并回答这个问题!

We can make use of mapped types to define the values of an object based on their key.我们可以使用映射类型来根据它们的键定义 object 的值。

type Dico<Keys extends string> = {
    [K in Keys]: TMyObject<K>
}

If we know all of the categories beforehand then we can create a map with those categories as keys.如果我们事先知道所有类别,那么我们可以创建一个 map,并将这些类别作为键。

const dico: Dico<'foo' | 'bar'> = {
  foo: { category: 'foo' }, // good
  bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
}

You could use Partial<Dico<Category>> if you want to limit it to a set of valid categories but not require all of them to be present.如果要将其限制为一组有效类别但不要求所有类别都存在,则可以使用Partial<Dico<Category>>

This makes sense only if your types are setup such that you already have an easy way to access a union type of all of the categories.仅当您的类型设置为您已经有一种简单的方法来访问所有类别的联合类型时,这才有意义。

We want to be able to just look at an object and see if its keys match its category strings.我们希望能够只查看 object 并查看其键是否与其类别字符串匹配。 We can do that, but only if we create the object through an identity function.我们可以这样做,但前提是我们通过身份 function 创建 object。 A generic function allows us to infer the Keys type from the object.一个通用的 function 允许我们从 object 推断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">

Typescript Playground Link Typescript 游乐场链接

As long as IMyObject has a cateogory of type string this is a runtime check, not a compile time check.只要IMyObject具有string类型的cateogory ,这就是运行时检查,而不是编译时检查。

You can enforce the check to be a compile time check with specific types for each of your cases:您可以将检查强制为每个案例的特定类型的编译时检查:

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!
};

Playground link 游乐场链接

You can also use the generic type, but in that case you need to pass it all the way like:您也可以使用泛型类型,但在这种情况下,您需要像这样一路传递它:

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
}

Playground link 游乐场链接

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM