Zanmato
Zanmato

Reputation: 1

How to resolve two or more String Literal union types?

I have two types of string literals:

type U = {
    type: "A1",
    value: number 
} | {
    type: "A2",
    value: string 
};
type V = {
    type: "A1",
    test: (value: number) => void;
} | {
    type: "A2",
    test: (value: string) => void;
}

In function resolve(), I'm trying to call method test() with value props from arguments type U:

let resolve = (u: U, v: V) => {
    if (u.type === v.type) {
        v.test(u.value)    
    }
}

But got error:

Argument of type 'string | number' is not assignable to parameter of type 'number & string'.
  Type 'string' is not assignable to type 'number & string'.
    Type 'string' is not assignable to type 'number'.

Some workaround, I have to separate check type into:

let resolve = (u: U, v: V) => {
    if ((u.type === "A1" && v.type === "A1")) {
        v.test(u.value)    
    } else if (u.type === "A2" && v.type === "A2") {
        v.test(u.value)
    }
}

Is it possible to declare a function without checking each of literal types?

Upvotes: 0

Views: 119

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249506

Typescript can't track related variables like this (something like jcalz's suggestion of correlated record types would be necessary for this).

The simplest solution is to use a type assertion since you know this to be valid:

let resolve = (u: U, v: V) => {
    if (u.type === v.type) {
        v.test(u.value as any)    
    }
}

Or a safer version that asserts to an intersection of value (this will ensure that all options are covered in U):

type UnionToIntersection<U> = 
    (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
let resolve = (u: U, v: V) => {
    if (u.type === v.type) {
        v.test(u.value as UnionToIntersection<U['value']>)    
    }
}

Upvotes: 1

Related Questions