Trying to create type which refers to field of some object.
Problem is that compiler can't detect that types of required argument and passed argument are the same. Looks like that Record type constraint is ignored.
Why does that happen? And how to change method declaration to make my intention clear for compiler (without using as/any and other less type safe techniques)?
#Why `Record` is ignored as type constraint?
9 messages · Page 1 of 1 (latest)
Preview:ts class Reference<Owner, Key extends keyof Owner> { constructor( private owner: Owner, private key: Key ) {} get(): Owner[Key] { return this.owner[this.key] } set(value: Owner[Key]): void { this.owner[this.key] = ...
You can choose specific lines to embed by selecting them before copying the link.
@torpid hull It's not that TS is ignoring the type constraint - I'm pretty sure TS is correct to reject this code.
I'm pretty sure this is one of the situations that works when the generics are left as their widest types but can run into issues when the generics have specific types:
const ex = new Reference<{ x: string }, "x">({ x: "foo" }, "x");;
const ex2 = new Reference<{ x: "specificString"}, "x">({ x: "specificString" }, "x");
// According to the types, this is valid, but would violate the types of ex2, which is only supposed to hold the value "specificString"
ex.swap<{ x: string }, "x">(ex2)
Here's a signature that works:
swap<OKey extends string>(reference: Reference<Record<OKey, Owner[Key]>, OKey>): void {
const temporary = this.get();
this.set(reference.get());
reference.set(temporary);
}
simplifying down to get rid of the second generic helps.
(Admittedly, this does accept the 'bad code' above, though ex2.swap(ex) is an error)
Here's the overall typing I'd probably use:
class Reference<Type, Key extends string> {
constructor(private owner: Record<Key, Type>, private key: Key) {
}
get(): Type {
return this.owner[this.key];
}
set(value: Type): void {
this.owner[this.key] = value;
}
swap<OKey extends string>(reference: Reference<Type, OKey>): void {
const temporary = this.get();
this.set(reference.get());
reference.set(temporary);
}
}
not sure it's any different than my previous example in practice, but it's a bit simpler.
@obsidian eagle Thank you for detailed explanation.