首页 > 解决方案 > 当 myRecord 已经输入为 Record 时如何使用 `keyof typeof myRecord`

问题描述

示例 1:

const myGroup = {
  Alice: { age: 5 },
  Bob: { age: 6 }, 
}
function getAge(name: keyof typeof myGroup) {
  return myGroup[name].age;
}
getAge('Charlie'); // error: name must be 'Alice' | 'Bob'

↑ 这就是我想要的,但是,如果我输入所有内容,它将不再起作用:

示例 2

interface People {
  age: number;
}

interface PeopleGroup {
  [name:string]: People;
}

const myGroup: PeopleGroup = {
  Alice: { age: 5 },
  Bob: { age: 6 }, 
}

function getAge(name: keyof typeof myGroup) {
  return myGroup[name].age;
}

getAge('Charlie'); // no error, name has type of string

我知道我可以使用enumor来解决它union type,但是在添加人员时需要编辑多个位置。在我的实际项目中,myGroup是一个结构复杂的大型硬编码字典,我希望既享受硬编码时的类型检查,又享受查询时的类型检查。有干燥的方法吗?

标签: typescript

解决方案


好的,问题是当您说某事时,Record<string, People>任何字符串键都是正确的,并且类型myGroup不会缩小到真正存在的键。为了在两个地方实现类型安全:

  • 在字典创建级别以具有适当的界面
  • 已将类型缩小到字典的确切键

我们需要引入值构造函数:

function makeGroup<T extends PeopleGroup>(input: T) {
  return input;
}
// using
const myGroup = makeGroup({
  Alice: { age: 5 },
  Bob: { age: 6 },
});

它所做的是它需要与接口匹配的东西PeopleGroup- 它是第一个类型安全元素,这意味着参数需要是PeopleGroup并返回窄类型。第二个非常重要,使用泛型类型表示我们要推断参数的确切类型的 TS。作为输出的结果,我们有确切的参数类型。

完整代码:

interface People {
  age: number;
}

interface PeopleGroup {
  [name:string]: People;
}

// value constructor
function makeGroup<T extends PeopleGroup>(input: T) {
  return input;
}

// correct creating of group - type safe for PeopleGroup
const myGroup = makeGroup({
  Alice: { age: 5 },
  Bob: { age: 6 },
});
const myGroupError = makeGroup({
  Alice: { age: 5 },
  Bob: { age: 'a' }, // error as it should be 
});

function getAge(name: keyof typeof myGroup) {
  return myGroup[name].age;
}

getAge('Alice') // ok !
getAge('Charlie'); // error as it should be 

操场


推荐阅读