I've found a workaround but actually the solution is not perfect:
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'
The idea is to create a simple object which implements the initial interface. We need this object in order to get its keys length for comparison in the isMyTypeArr
Type Guard. Type Guard simply compare the length of arrays - if they have the same length, it means that you provide all properties.
Edit
Added another similar (more generic) solution - the main differences are:
- use class with constructor params which implements the initial interface;
- this class has
length
property (because basically it's a constructor function) we can use it in our Type Guard;
- we also have to pass class name as a second parameter in order to get it constructor args length. We cannot use generic type
T
for this, because the compiled JS has all the type information erased, we can't use T
for our purpose, check this post for more deta
So this is the final solution:
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. also created a stackblitz demo of both examples