使通用类型 Array<keyof T> 需要 T 的所有键
Make a generic type Array<keyof T> require all keys of T
我想声明一个类型,要求给定类型 T
的所有键都包含在一个数组中,例如:
checkKeys<T>(arr: Array<keyof T>): void {
// do something
}
interface MyType {
id: string;
value: number;
}
目前如果调用checkKeys<MyType>
,TS将认为传递的值如果包含MyType
(id | value
)的任何键是有效的:
checkKeys<MyType>(['id', 'value']); // valid
checkKeys<MyType>(['id']); // valid
checkKeys<MyType>(['id', 'values']); // invalid
是否可以要求数组中指定所有键?
你不能用数组类型来做到这一点(至少我不知道有一种方法可以将键的并集扩展到元组类型中,可能有一种我只是不知道)。另一种方法是使用对象文字来实现类似的效果。语法有点冗长,但编译器将验证是否只指定了正确的键。我们将使用 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
我找到了一个解决方法,但实际上这个解决方案并不完美:
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'
想法是创建一个实现初始接口的简单对象。我们需要这个对象来获取它的键长度,以便在 isMyTypeArr
Type Guard 中进行比较。 Type Guard 只是比较数组的长度——如果它们具有相同的长度,则意味着你提供了所有属性。
编辑
添加了另一个类似(更通用)的解决方案 - 主要区别是:
- 将class与实现初始接口的构造函数参数一起使用;
- 这个class有
length
属性(因为基本上它是一个构造函数)我们可以在我们的类型保护中使用它;
- 我们还必须将 class 名称作为第二个参数传递,以获取其构造函数参数长度。我们不能为此使用泛型类型
T
,因为编译后的JS已将所有类型信息都删除,我们不能使用T
,check this post for more deta
所以这是最终的解决方案:
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'
Notice that these examples are based on TypeScript issue 13267
p.s。还创建了两个示例的 stackblitz demo
我想声明一个类型,要求给定类型 T
的所有键都包含在一个数组中,例如:
checkKeys<T>(arr: Array<keyof T>): void {
// do something
}
interface MyType {
id: string;
value: number;
}
目前如果调用checkKeys<MyType>
,TS将认为传递的值如果包含MyType
(id | value
)的任何键是有效的:
checkKeys<MyType>(['id', 'value']); // valid
checkKeys<MyType>(['id']); // valid
checkKeys<MyType>(['id', 'values']); // invalid
是否可以要求数组中指定所有键?
你不能用数组类型来做到这一点(至少我不知道有一种方法可以将键的并集扩展到元组类型中,可能有一种我只是不知道)。另一种方法是使用对象文字来实现类似的效果。语法有点冗长,但编译器将验证是否只指定了正确的键。我们将使用 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
我找到了一个解决方法,但实际上这个解决方案并不完美:
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'
想法是创建一个实现初始接口的简单对象。我们需要这个对象来获取它的键长度,以便在 isMyTypeArr
Type Guard 中进行比较。 Type Guard 只是比较数组的长度——如果它们具有相同的长度,则意味着你提供了所有属性。
编辑
添加了另一个类似(更通用)的解决方案 - 主要区别是:
- 将class与实现初始接口的构造函数参数一起使用;
- 这个class有
length
属性(因为基本上它是一个构造函数)我们可以在我们的类型保护中使用它; - 我们还必须将 class 名称作为第二个参数传递,以获取其构造函数参数长度。我们不能为此使用泛型类型
T
,因为编译后的JS已将所有类型信息都删除,我们不能使用T
,check this post for more deta
所以这是最终的解决方案:
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'
Notice that these examples are based on TypeScript issue 13267
p.s。还创建了两个示例的 stackblitz demo