映射对象类型的类型安全扩展

时间:2019-04-29 12:37:02

标签: typescript

想象一下您有一个这样的类型:

type Person = {
  email: string
  name: string
  passport: { series: string, number: string } // or just Passport type
}

现在您想在某个地方拥有一组禁用的键,如下所示:

const disabledKeys = {
  email: true,
  passport: {
    series: true
  }
}

所以我从该类型开始使用DisabledKeys:

type DisabledKeys = {
  [key in keyof Person]?: boolean
}

但是我想描述passport的正确形状,所以我做了以下工作:

type DisabledKeys = { [key in keyof Person]?: boolean } & {
  passport: Record<keyof Passport, boolean>
  foo: number // <---- `foo` isn't a part of Person type
}

如何更好地实现此扩展部分?我希望它是类型安全的,并且仅包含Person类型的键。但目前它可能包含任何键。例如,如果我亲自将passport重命名为其他名称,我将不会收到编译时错误。

1 个答案:

答案 0 :(得分:2)

我认为您可能在这里寻找的是mapped类型的conditional属性类型:

type NestedDisabledKeys<T> = { [K in keyof T]?:
  T[K] extends object ? NestedDisabledKeys<T[K]> : true;
}

type DisabledKeys = NestedDisabledKeys<Person>

类型NestedDisabledKeys递归地遍历所有对象类型的属性。对于非对象属性,它允许缺少属性(因此所有道具都是可选的)或值true(我假设您从未打算将false放在其中,但是如果这样做,您可以像在代码中那样将true更改为boolean。这样会自动构建您要查找的结构,而不必担心手工正确或不正确地构造。

让我们看看它的作用:

const disabledKeys: DisabledKeys = {
  email: true,
  passport: {
    series: true
  }
}

const badDisabledKeys: DisabledKeys = {
  email: true,
  passpork: {  // error! did you mean "passport"?
    series: true
  } 
}

const badDisabledKeys2: DisabledKeys = {
  email: true,
  passport: { 
    series: true
  },
  foo: true; // error! "foo" is not expected in obj literal
}


const badDisabledKeys3: DisabledKeys = {
  email: true,
  passport: {  
    series: true,
    nomber: true, // error! did you mean "number"?    
  } 
}

这对您有用吗?这里有一个皱纹,甚至可能缺少对象属性键,例如:

const okayDisabledKeys: DisabledKeys = {
  email: true
}

我认为这是可以接受的,并且意味着passport 包括所有子属性均未禁用,对吗?如果要要求指定每个对象类型的键,则答案将更加复杂。

无论如何,希望能有所帮助;祝你好运!