10

我想声明一个需要给定类型的所有键都T包含在数组中的类型,例如:

checkKeys<T>(arr: Array<keyof T>): void {
  // do something
}

interface MyType {
  id: string;
  value: number;
}

目前,如果一个 call checkKeys<MyType>,TS 将认为传递的值如果它包含MyType( id | value) 的任何键是有效的:

checkKeys<MyType>(['id', 'value']); // valid

checkKeys<MyType>(['id']); // valid

checkKeys<MyType>(['id', 'values']); // invalid

是否可以要求在数组中指定所有键?

4

2 回答 2

5

你不能用数组类型来做到这一点(至少我不知道一种将键的联合传播到元组类型的方法,可能有一个我只是不知道)。另一种方法是使用对象文字来实现类似的效果。语法有点冗长,但编译器将验证是否只指定了正确的键。我们将使用Record映射类型,并且我们可以将0文字类型用于值,因为只有键很重要。

function checkKeys<T>(o: Record<keyof T, 0>): void {
     // do something
}

interface MyType {
    id: string;
    value: number;
}

checkKeys<MyType>({ id: 0, value: 0 }); // valid

checkKeys<MyType>({ id: 0 }); // invalid

checkKeys<MyType>({ id: 0, values: 0 }); // invalid
于 2018-08-26T19:13:03.177 回答
2

我找到了一种解决方法,但实际上解决方案并不完美:

interface MyType {
  id: string;
  value: number;
}
const myType: MyType = {
   id: '',
   value: 0
};
type ArrType<T> = Array<keyof T>;
function isMyTypeArr<T>(arg: any[]): arg is ArrType<T> {
  return arg.length === Object.keys(myType).length;
}

function checkKeys<T>(arr: ArrType<T>): void {
  if (isMyTypeArr(arr)) {
    console.log(arr.length);
    // some other stuff
  }
}
checkKeys<MyType>(['id', 'x']); // TS error
checkKeys<MyType>(['id']); // no console because of Type Guard
checkKeys<MyType>(['id', 'value']); // SUCCESS: console logs '2'

这个想法是创建一个实现初始接口的简单对象。我们需要这个对象来获取它的密钥长度,以便在isMyTypeArrType Guard 中进行比较。Type Guard 只是比较数组的长度 - 如果它们具有相同的长度,则意味着您提供了所有属性。


编辑

添加了另一个类似(更通用)的解决方案 - 主要区别是:

  • 使用带有实现初始接口的构造函数参数的类;
  • 这个类有length属性(因为它基本上是一个构造函数)我们可以在我们的类型保护中使用它;
  • 我们还必须将类名作为第二个参数传递,以获取它的构造函数参数长度。我们不能T为此使用泛型类型,因为编译后的 JS 已经删除了所有类型信息,我们不能T用于我们的目的,查看这篇文章以获得更多详细信息

所以这是最终的解决方案:

interface IMyType {
  id: string;
  value: number;
}
class MyType implements IMyType {
  constructor(public id: string = '', public value: number = 0) {}
}
type ArrType<T> = Array<keyof T>;
function isMyTypeArr<T>(arg: ArrType<T>, TClass: new () => T): arg is ArrType<T> {
  return arg.length === TClass.length;
}

function checkKeys<T>(arr: ArrType<T>, TClass: new () => T): void {
  if (isMyTypeArr<T>(arr, TClass)) {
    console.log(arr.length);
    // some other stuff
  }
}

checkKeys<MyType>(['id', 'x'], MyType); // TS error
checkKeys<MyType>(['id'], MyType); // no console because of Type Guard
checkKeys<MyType>(['id', 'value'], MyType); // SUCCESS: console logs '2'

请注意,这些示例基于TypeScript 问题 13267

ps 还创建了两个示例的stackblitz 演示

于 2018-08-26T20:06:12.647 回答