danielvolchek
danielvolchek

Reputation: 115

Conditional typing with generic types

Given some types that looks like this

type Priority = "HIGH" | "MED" | "LOW";

type Task = {
title: string;
desc: string;
priority: Priority;
}

interface Edit<T> {
    param: T;
    change: T extends "title" | "desc"
        ? string
        : T extends "priority"
        ? Priority
        : never;
}

export type ParamsType<T> = T extends "add"
    ? Add
    : T extends "help"
    ? Help
    : T extends "edit"
    ? Edit<keyof Task>
    : never;

used in a function like this

exec<T extends Params>(command: T, args: ParamsType<T>) {
    // console.log(this);
    switch (command) {
        case "add":
            // call add
            break;
        case "edit":
            // call edit
            break;
     }
}

How can I get type inference for change based on a call like this?

schedule.exec("edit", {param: "priority", change: "notintype"}

I am getting type inference based on the value passed as the first parameter so how could I adapt that to getting the change to be typed as well?

Upvotes: 1

Views: 47

Answers (1)

Oblosys
Oblosys

Reputation: 15106

You can probably get the behavior you want by rewriting Edit to distribute over unions, so Edit<'title' | 'desc' | 'priority'> is evaluated as Edit<'title'> | Edit<'desc'> | Edit<'priority'>, which will provide the proper type checking.

To have Edit distribute over unions, you can convert it to a type definition and introduce a dummy conditional (see docs):

type Edit<T> = T extends any ? {
    param: T;
    change: T extends "title" | "desc"
        ? string
        : T extends "priority"
        ? Priority
        : never;
} : never

This will allow calls like

schedule.exec("edit", {param: "priority", change: "HIGH"})
schedule.exec("edit", {param: "title", change: "Title"})

but fail with a type error on

schedule.exec("edit", {param: "priority", change: "notintype"})

TypeScript playground

Upvotes: 1

Related Questions