Consider the following example:
type A = {
a: string;
name: string;
}
type B = {
b: string;
name: string
}
function update<T extends A | B>(aOrB: T): T {
return {...aOrB, name: "updated"}
}
const updatedA = update<A>({a: "a", name: "a"})
const updatedB = update<B>({b: "b", name: "B"})
How can we change this so that TypeScript can catch type errors within the update
function?
Example type errors below that aren't being caught:
function update<T extends A | B>(aOrB: T): T {
return {...aOrB, b: 2, name: "updated"}
}
function update<T extends A | B>(aOrB: T): T {
return {...aOrB, foo: 2, name: "updated"}
}
I would expect that in each of the examples above that there would be a type error. In the first example, b
is not guaranteed to exist, and if it does exist, it's a string, not a number. In the second, foo
is not a field on either type.
CodePudding user response:
Instead of a generic type in the returned value, you can achieve it with a returned type A|B
Basically, a generic type cannot determine which type you want to have in the returned result, so you need to set a certain type for it
type A = {
a: string;
name: string;
}
type B = {
b: string;
name: string
}
function update<T extends A | B>(aOrB: T): (A|B) {
return {...aOrB, b: 2, name: "updated"} //error
}
const updatedA = update<A>({a: "a", name: "a"})
const updatedB = update<B>({b: "b", name: "B"})
Another way we can try is having hasOwnProperty
to check property in the data, and then using them to cast to a proper type. It's kind of messy, but we would know types beforehand.
type A = {
a: string;
name: string;
}
type B = {
b: string;
name: string
}
function update<T extends A | B>(aOrB: T): (A|B) {
if(aOrB.hasOwnProperty("b")) {
return {...aOrB, a: "new", name: "updated"} as B //error
}
return {...aOrB, a: "new", name: "updated"} as A
}
const updatedA = update<A>({a: "a", name: "a"})
const updatedB = update<B>({b: "b", name: "B"})