Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/472.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript TypeScript:在不显式定义键的情况下创建类型化记录_Javascript_Typescript - Fatal编程技术网

Javascript TypeScript:在不显式定义键的情况下创建类型化记录

Javascript TypeScript:在不显式定义键的情况下创建类型化记录,javascript,typescript,Javascript,Typescript,我想做的是创建一个对象,它是某个接口的记录,并且让TypeScript能够根据对象中的内容推断出键。我试过一些方法,但没有一种能完全满足我的要求 interface Person { firstName:string } const peopleObj = { Jason: { firstName: "Jason" }, Tim: { firstName: "Tim" } } as const; console.lo

我想做的是创建一个对象,它是某个接口的记录,并且让TypeScript能够根据对象中的内容推断出键。我试过一些方法,但没有一种能完全满足我的要求

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);