Reputation: 3509
Curious how I would get the following to work in typescript. tempA
copies x.a to x.temp and returns it, and tempB
does the same to x.b. Now, for whatever reason calling tempA
makes the compiler seem to forget that there's a b
field on x
. Is there a way to get past this? Or is there a github issue tracking this pattern / what feature name would it be filed under?
let tempA = (x: { a: number; temp?: number}) => {
x.temp = x.a;
return x;
}
let tempB = (x: { b: number; temp?: number}) => {
x.temp = x.b
return x;
}
let x = { a: 4, b: 3 };
let y = tempA(x);
let z = tempB(y); // Can't! y.b does not exist (even though it does)
Upvotes: 2
Views: 60
Reputation: 51112
Since the parameter type of x
is declared as { a: number; temp?: number }
, and the function returns x
, the return type is inferred as { a: number, temp?: number }
. Therefore, that's the type of y
, so Typescript doesn't know that y
has a property named b
.
You can solve this by making the function generic, so it guarantees to return something of the same type as its argument:
function tempA<T extends { a: number; temp?: number }>(x: T): T {
x.temp = x.a;
return x;
}
That's good enough for your sample code, but not as strict as possible; the return type suggests that the object might not have a temp
property, when in reality we can guarantee that it does have this property. We can write a stricter return type using an intersection &
; this is a bit messier, so you may or may not prefer it depending on your requirements.
function tempA<T extends { a: number; temp?: number }>(x: T): T & { temp: number }
{
x.temp = x.a;
return x as T & { temp: number };
}
Upvotes: 3
Reputation: 3476
Here, x has the following (inferred) type signature:
let x: {a: number, b: number} = {a: 4, b: 3}
After this, you pass it into a method tempA
which has the following inferred type signature:
(x: { a: number; temp?: number }) => { a: number; temp?: number }
Now you see the problem. When you write,
let y: { a: number; temp?: number } = tempA(x);
obviously "b" is missing. This is not a bug or missing feature. Consider the following case:
let y = tempA({ a: 4 });
// Suppress compiler warnings
// @ts-ignore
let z: { b: number; temp?: number} = tempB(y)
// z.b = undefined, violates type constraints
To solve your issue, you'll need to fundamentally change your type signatures.
// Guarantee that any 'x' passed to tempA will have a 'b' property
let tempA = (x: { a: number; b: number; temp?: number}) => {
x.temp = x.a;
return x;
}
let tempB = (x: { b: number; temp?: number}) => {
x.temp = x.b
return x;
}
let x = { a: 4, b: 3 };
let y = tempA(x);
let z = tempB(y); // Works :D
Upvotes: 0