简体   繁体   English

是否可以在不为其键显式设置类型的情况下强制执行对象值的类型?

[英]Is it possible to enforce the type of an object's values without explicitly setting a type for its keys?

I want something like this:我想要这样的东西:

type Person = { age:number, weight: number}
const people:{[key:infer_the_type]:Person} = {
  mike: {...},
  bob: {...},
}

so that if I do this, it works:所以如果我这样做,它会起作用:

keyof typeof people // This should be "mike" | "bob"

Is this possible?这可能吗?

You cannot get the behavior you're looking for by annotating the type of people ;你不能得到你正在寻找的行为注释类型people ; once you annotate people with a (non-union) type, the compiler will automatically throw away any more specific information it had about the initialized value.一旦您使用(非联合)类型注释people ,编译器将自动丢弃它拥有的有关初始化值的任何更具体的信息。 There's a (longstanding) open issue in GitHub, microsoft/TypeScript#7481 which asks for something like your infer_the_type (especially relevant is microsoft/TypeScript#38349 which was closed as a duplicate). GitHub 中有一个(长期存在的)未解决问题microsoft/TypeScript#7481 ,它要求提供类似infer_the_type (尤其相关的是microsoft/TypeScript#38349 ,它已作为副本关闭)。 If you want to see that happen, you might want to go to that issue and give it a 👍, but it's not clear when or if this will ever be implemented.如果你想看到这种情况发生,你可能想要去那个问题并给它一个 👍,但目前尚不清楚这何时或是否会实施。

Instead, you can get similar behavior by replacing your type annotation with a generic helper function:相反,您可以通过使用通用辅助函数替换类型注释来获得类似的行为:

const asPeople = 
  <K extends PropertyKey>(people: { [P in K]: Person }) => people;

const people = asPeople({
  mike: { name: "Mike", weight: 150 },
  bob: { name: "Bob", weight: 200 },
}); // okay

You can verify that the compiler has inferred the keys of people :您可以验证编译器是否已推断出people的密钥:

/* const people: {
    mike: Person;
    bob: Person;
} */

And the helper function will report an error if you pass it something unexpected:如果你传递了一些意想不到的东西,辅助函数将报告错误:

const badPeople = asPeople({
  alice: { name: "Alice", height: 75 } // error!
  // -------------------> ~~~~~~~~~~
  //  Did you mean to write 'weight'?
})

Playground link to code Playground 链接到代码

I would be tempted to create a helper function to do this.我很想创建一个辅助函数来做到这一点。 It's a little bit ugly since it means having pointless functions floating around at runtime but will get the job done:这有点难看,因为它意味着在运行时有无意义的函数漂浮,但会完成工作:

const getPeople = <P extends PropertyKey>(p: Record<P, Person>) => p;

const typedPeople = getPeople({
  mike: {name: "mike", weight: 32},
  bob: {name: "bob", weight: 6},
})

typedPeople will be of type Record<'mike' | 'bob', Person> typedPeople的类型为Record<'mike' | 'bob', Person> Record<'mike' | 'bob', Person> . Record<'mike' | 'bob', Person>

Alternatively, since this is presumably a static list of names (I don't see how this would work otherwise), it might just be better to create the type beforehand, assming the list is not too long or subject to change:或者,由于这可能是一个静态名称列表(我不知道这会如何工作),最好事先创建类型,使列表不要太长或可能会发生变化:

type Names = 'mike' | 'bob';

Even more alternatively, I am a little suspicious of the need to explicitly type people at all.更重要的是,我对明确输入people的必要性有点怀疑。 As you've already demonstrated, you can use keyof to get the names out of the object after instantiating it, so anywhere it's going to get used should have the types already available.正如您已经演示的那样,您可以在实例化对象后使用keyof从对象中获取名称,因此在任何要使用它的地方都应该有可用的类型。

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

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