I'm trying to figure out why the following code will not work as I expect it to. The intersection type seems to work fine (when redefined inside of the function) however using it as the default to a generic argument type does not.
interface A {
a: string
}
interface B {
b: string
}
interface C {
c: string
}
function returnStuff<T = A & B>(
optionalReturnFn?: (value: T) => T): (value: T) => T {
const defaultReturnFn = (value: T) => {
value.a // <----- why does this a error
return value
}
return optionalReturnFn ? optionalReturnFn : defaultReturnFn
}
function returnStuff2<T extends A & B>(
optionalReturnFn?: (value: T) => T): (value: T) => T {
const defaultReturnFn = (value: T) => {
value.a // <----- while this one does not
return value
}
return optionalReturnFn ? optionalReturnFn : defaultReturnFn
}
const bc: B & C = { b: 'b', c: 'c' } // <--- valid intersection type
// Works as expected when calling fn but DOESN't work as I'd expect internally to fn
returnStuff<B & C>((value) => {
return value
})
// Doesn't work when calling fn but DOES work as expected internally to fn
returnStuff2<B & C>((value) => {
return value
})
Here is a TS Playground if you'd like to play around with the errors.
UPDATE: I ended up adding a type guard and using it in the returnStuff function definition.
...
function isDefaultType<DefaultType>(
record: any,
keys: string[]
): record is DefaultType {
return every(keys, (key) => key in record)
}
function returnStuff<T = A & B>(
optionalReturnFn?: (value: T) => T): (value: T) => T {
const defaultReturnFn = (value: T) => {
if(isDefaultType<T>(value, ['a', 'b']) {
value.a // <----- no longer throws an error
return value
}
}
return optionalReturnFn ? optionalReturnFn : defaultReturnFn
}
returnStuff
T = A & B shows the error beside value.a because you've only set a default value, but you didn't limit types of T via extends, T can still be anything, but if it wasn't specified it would be A & B.
returnStuff<C>(v => v); - works. returnStuff(v => v); - works too. value.a fails because value can be anything, string, undefined etc.
the right way would be T extends A & B = A & B
returnStuff2
returnStuff2<B & C> doesn't work because T extends A & B (not a default value, but a defined base type) requires a property which is missed in union of B and C.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With