Javascript TypeScript:在不显式定义键的情况下创建类型化记录
我想做的是创建一个对象,它是某个接口的记录,并且让TypeScript能够根据对象中的内容推断出键。我试过一些方法,但没有一种能完全满足我的要求Javascript TypeScript:在不显式定义键的情况下创建类型化记录,javascript,typescript,Javascript,Typescript,我想做的是创建一个对象,它是某个接口的记录,并且让TypeScript能够根据对象中的内容推断出键。我试过一些方法,但没有一种能完全满足我的要求 interface Person { firstName:string } const peopleObj = { Jason: { firstName: "Jason" }, Tim: { firstName: "Tim" } } as const; console.lo
interface Person {
firstName:string
}
const peopleObj = {
Jason: {
firstName: "Jason"
},
Tim: {
firstName: "Tim"
}
} as const;
console.log(peopleObj);
在这里,如果你看peopleObj,TypeScript知道确切的键,因为为const
。这里的问题是我没有强制每个对象都是人
。所以我下一步尝试了这个:
const peopleObj: Record<string, Person> = {
Jason: {
firstName: "Jason"
},
Tim: {
firstName: "Tim"
}
} as const;
我相信您的第三种方法实际上不起作用,因为您可以毫无错误地访问People.foo。这是因为当您将peopleObj构造为
记录时,它的类型现在是。然后执行记录的keyof
,其计算结果为字符串
我所知道的实现这一点的唯一方法是使用带有泛型的函数。这允许您对输入参数应用约束,然后返回原始类型
constcreatepeople=(people:T)=>people;
const myPeople=createPeople({
杰森:{
名字:“杰森”
},
蒂姆:{
名字:“蒂姆”
}
});
console.log(myPeople.Jason);
console.log(myPeople.foo);//错误
你有一个第22条军规的情况,否则-即-我想强制我的钥匙是Person类型的,但我不知道我的钥匙是什么
您可能更喜欢的另一种方式-基本相同,但没有功能:
接口人{
名字:string
}
//从类型到扩展形式的强制评估
类型EvaluateType=T扩展推断O?{[K in keyof]:O[K]}:永远不会;
类型PeopleType=EvaluateType;
常量peopleLookup={
杰森:{
名字:“杰森”
},
蒂姆:{
名字:“蒂姆”
}
};
const people:PeopleType=peopleLookup;
console.log(people.Jason);
console.log(people.foo);
我在扩展@mbdavis的答案,因为似乎还有一个额外的限制,那就是记录中每个人的关键是人的名字属性。这允许我们使用映射类型
PeopleRecord
应该是这样一个对象:对于每个键name
,值要么是Person
对象,且{firstName:name}
或未定义
type PeopleRecord = {
[K in string]?: Person & {firstName: K}
}
不幸的是,如果我们只是将此类型应用于peopleObj
,它就不能按我们希望的方式工作,因为它不会将名称视为比string
更具体的名称。也许其他用户可以想出如何使它工作,但我不能
像@mbdavis一样,我需要重新分配对象以强制它匹配更具体的约束
我使用映射类型ValidateRecord
,它接受类型T
,并强制该类型上的每个键都是具有该名字的Person
type ValidateRecord<T> = {
[P in keyof T]: Person & {firstName: P}
}
但是通过一个函数做这个断言要干净得多。请注意,函数本身仅返回输入。您可以在这里的JS端执行任何其他验证检查
const asValid = <T extends {}>( record: T & ValidateRecord<T> ): ValidateRecord<T> => record;
为了简单起见,这是一个有点做作的例子。函数方法确实可以满足我的需要,我只是想知道是否有一种不需要函数的方法。如果没有,这是有效的,谢谢!是的,这是无可争辩的。我意识到还有一种选择——它涉及到复制类型,请参见您对“纯接口”问题和解决方案的看法,请参见
const validatedObj: ValidateRecord<typeof peopleObj> = peopleObj;
const asValid = <T extends {}>( record: T & ValidateRecord<T> ): ValidateRecord<T> => record;
const goodObj = {
Jason: {
firstName: "Jason"
},
Tim: {
firstName: "Tim"
}
} as const;
// should be ok
const myRecordGood = asValid(goodObj);
// should be a person with {firstName: "Tim"}
const A = myRecordGood["Tim"]
// should be a problem
const B = myRecordGood["Missing"]
const badObj = {
Jason: {
firstName: "Bob"
},
Tim: {
firstName: "John"
}
} as const;
// should be a problem
const myRecordBad = asValid(badObj);