Basically what I want to achieve is to have class X infer its K type from the provided constructor argument typing.
I have written this:
interface XContstructorParams<K extends string = never> {
record?: Record<K, number>;
// Other stuff with some being optionals, which is why it's all in single
// object, as I don't want to do new X(arg1, undefined, undefined, arg2)
}
class X<K extends string = never> {
private record: Record<K, number>;
constructor(params: XContstructorParams<K>) {
// I'd rather avoid to use a type assertion here but it's not a big deal
// compared to the issue below.
this.record = params.record || {} as Record<K, number>;
}
getNumber(k: K): number {
return this.record[k];
}
}
This actually works to infer the type of K to be never when the record
property is not given, but it does not prevent to manually specify an incorrect value for K:
// Correctly inferred to be X<'a', 'b'>
const x1 = new X({ record: {'a': 1, 'b': 2} });
// Correctly inferred to be X<never>
const x2 = new X({});
// Oops ! This is incorrect as `getNumber` will not return a number when called with 'c'
// or 'd ! But it is allowed because of how I wrote the constructor `record` property to
// be optional. I want it to be optional only when K is never. It's either you provided
// a record and it's keys determine K, or you didn't give a record and K is never, but
// should not be able to specify K and not provide a record matching your specified K.
const x3 = new X<'c' | 'd'>({});
I'd like x3 to be recognized as a compilation error but I couldn't manage to make it work. What am I doing wrong?
EDIT: for some reason, stack overflow removes my "hello" at the start of the message so... better late than never I guess... Hello and thanks for your help; ;-)
Is this solution works for you?
interface XContstructorParams<K extends string = never> {
record: K extends never ? undefined : Record<K, number>;
// Other stuff with some being optionals, which is why it's all in single
// object, as I don't want to do new X(arg1, undefined, undefined, arg2)
}
class X<K extends string = never> {
private record: Record<K, number>;
constructor(params: XContstructorParams<K>) {
// I don't think you need type castin here
this.record = params.record
}
getNumber(k: K): number {
return this.record[k];
}
}
// Correctly inferred to be X<'a', 'b'>
const x1 = new X({ record: { 'a': 1, 'b': 2 } });
const result = x1.getNumber('a') // number
const x2 = new X<never>({}); // error
const x3 = new X<'c' | 'd'>({}); // error
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.