Home > front end >  Typescript generics with conditional types
Typescript generics with conditional types

Time:09-17

I have a type containing some conditionals (Note this isn't my actual code, but a stripped down version which tries to highlight the issue with the least amount of code)

type A<T> = {
  prop: T;
  value:
    T extends string ? { a: T, b: T } :
    T extends number ? { y: T, z: T } :
    never
}

Instantiating variables with this type works fine. E.g.

// works
const a_string: A<string> = {
  prop: '',
  value: { a: '', b: ''}
}

// works
const a_number: A<number> = {
  prop: 0,
  value: { y: 1, z: 1}
}

// errors (as expected)
const a_mistake: A<number> = {
  prop: 0,
  value: { a: '', b: '' } <-- Should be { y:number, z: number }
}

My problem lies when I try and create this function:

function myFunc<T extends string>(prop: T): A<T> {
  return {
    prop,
    value: { a: prop, b: prop }
  }
}

I cannot understand why this does not work. Typescript complains that what I am returning is not assignable to A:

Type '{ a: T; b: T; }' is not assignable to type 'T extends string ? { a: T; b: T; } : T extends number ? { y: T; z: T; } : never'.ts(2322)
union.ts(33, 3): The expected type comes from property 'value' which is declared here on type 'A<T>'

For the life of me I cannot understand why?

CodePudding user response:

I don't know the answer to why this doesn't work, but for some reason typescript is not able to infer the correct type of value property. A way you can solve this is by using type assertions, since you know that the prop extends string:

function myFunc<T extends string>(prop: T) {
    return {
        prop,
        value: { a: prop, b: prop },
    } as A<T>;
}

CodePudding user response:

I have overenginered a bit.

All you need to do is to overalod your function.

type A<T> = {
    prop: T;
    value:
    T extends string ? { a: T, b: T } :
    T extends number ? { y: T, z: T } :
    never
}

function myFunc<Prop extends string | number>(prop: Prop): A<Prop>
function myFunc<Prop extends string | number>(prop: Prop) {
    return {
        prop,
        value: { a: prop, b: prop }
    }
}


const result = myFunc('sdf').value.a // ok
const result2 = myFunc(42).value.y // ok

Playground

I assume you have a lot of switch cases in this function, otherwise you don't even need a generic parameter.

  • Related