使泛型类型Array <keyof t =“”>需要T的所有键

时间:2018-08-26 17:43:13

标签: typescript generics keyof

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

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

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

当前,如果调用checkKeys<MyType>,则TS如果包含MyTypeid | value)的任何键,则将认为传递的值有效:

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

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

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

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

2 个答案:

答案 0 :(得分:3)

您不能使用数组类型来做到这一点(至少我不知道一种将键的并集扩展为元组类型的方法,可能我只是不知道一种方法)。一种替代方法是使用对象文字来实现类似的效果。语法较为冗长,但是编译器将验证仅指定了正确的键。我们将使用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

答案 1 :(得分:2)

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

isMyTypeArr

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


修改

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

  • 使用带有构造函数参数的类来实现初始接口;
  • 此类具有T属性(因为基本上是构造函数),我们可以在Type Guard中使用它;
  • 我们还必须将类名作为第二个参数传递,以获取构造函数的args长度。我们不能为此使用通用类型T,因为已编译的JS删除了所有类型信息,因此我们无法使用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' 来实现check this post for more deta

这是最终的解决方案:

LayoutDocument layoutDocument = new LayoutDocument { Title = "Plan Layout" };

Window mainWindow = Application.Current.Windows.OfType<Window>().Where(x => x.Name == "MainWindow").FirstOrDefault();
if (mainWindow != null)
{
    mainWindow.mainPanel.Children.Add(layoutDocument);
}
  

请注意,这些示例均基于TypeScript issue 13267

p.s。还创建了两个示例的stackblitz demo