Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to default generic function arguments with intersection types

Tags:

typescript

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
}
like image 542
Alec Sibilia Avatar asked Jul 24 '26 16:07

Alec Sibilia


1 Answers

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.

like image 134
satanTime Avatar answered Jul 27 '26 07:07

satanTime



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!