Reputation: 3642
I know there are some posts related, however, I don't get to accomplish that:
I want to type guard and object which has a generic key to verify that is a string for a dictionary. Is it possible?
interface IDictionary<T> {
[key: string]: T | undefined;
}
class HashArray<T, E extends keyof T & string> {
private key: E;
private dict: IDictionary<T>;
private arr: T[];
constructor(theyKey: E) {
this.key = theyKey;
this.dict = {};
this.arr = [];
}
public push(elem: T) {
this.arr.push(elem);
if (this.isValidKey(elem[this.key])) this.dict[elem[this.key]] = elem; // Error
}
private isValidKey(key: T[E]): key is string { // Error
return typeof key === "string";
}
}
class Sample {
oneKey: string = "1";
anotherKey: number = 0;
}
const oneKey = new HashArray<Sample, 'oneKey'>('oneKey');
const secondKey = new HashArray<Sample, 'anotherKey'>('anotherKey');
oneKey.push(new Sample()); // Works
secondKey.push(new Sample()); // It should fail because anotherKey is number
The compilation shows two errors:
Type 'T[E]' cannot be used to index type 'IDictionary'
A type predicate's type must be assignable to its parameter's type. Type 'string' is not assignable to type 'T[E]'
How can I avoid that error without disable it?
Upvotes: 2
Views: 1332
Reputation: 249656
Since you want to narrow the type of elem
that is what you need to pass to the type guard. Also if you want T[E]
to be of type string keyof T & string
is not the way to do it, you will need a more complex mapped conditional type, you can read here about it
interface IDictionary<T> {
[key: string]: T | undefined;
}
type KeyOfType<T, V> = {
[P in keyof T]-?: T[P] extends V ? P : never
}[keyof T];
class HashArray<T, E extends KeyOfType<T, string>> {
private key: E;
private dict: IDictionary<T>;
private arr: T[];
constructor(theyKey: E) {
this.key = theyKey;
this.dict = {};
this.arr = [];
}
public push(elem: T) {
this.arr.push(elem);
if (this.isValidKey(elem)) this.dict[elem[this.key]] = elem;
}
private isValidKey(elem: T): elem is T & Record<E, string> {
return typeof elem[this.key] === "string";
}
}
class Sample {
oneKey: string = "1";
anotherKey: number = 0;
}
const oneKey = new HashArray<Sample, "oneKey">("oneKey");
const secondKey = new HashArray<Sample, "anotherKey">("anotherKey"); // Fails here, Sample["anotherKey"] is not a string
oneKey.push(new Sample()); // Works
secondKey.push(new Sample());
Upvotes: 2