Home > Net >  Dynamic type with permutation in TypeScript
Dynamic type with permutation in TypeScript

Time:10-30

I'd like to create dynamic types excluding duplicates. This is what I have right now:

type Currency = 'CAD' | 'USD' | 'EUR';
type CurrencyQuote = `${Currency}x${Currency}`; // returns:  "CADxUSD" | "USDxCAD" | "CADxCAD" | "CADxEUR" | "USDxUSD" | "USDxEUR" | "EURxCAD" | "EURxUSD" | "EURxEUR"

How could I exclude the ones that are duplicated, like USD|USD, CAD|CAD and EUR|EUR?

Thanks

CodePudding user response:

We could use a mapped type which maps over the elements in Currency and constructs the template string literal consisting of the current element K and Exclude<Currency, K>.

Since the mapped type returns an object with the currencies as properties, we need to index this type with Currency to get the union of the types of all keys.

type CurrencyQuote = {
    [K in Currency]: `${K}x${Exclude<Currency, K>}`
}[Currency]

// type CurrencyQuote = 
//   | "CADxUSD" 
//   | "CADxEUR"
//   | "USDxCAD" 
//   | "USDxEUR" 
//   | "EURxCAD" 
//   | "EURxUSD"

You might want a reusable utility type to do this.

type ConcatWithoutDupes<T extends string> = {
    [K in T]: `${K}x${Exclude<T, K>}`
}[T]

type CurrencyQuote = ConcatWithoutDupes<Currency>

Playground

CodePudding user response:

You could make a distributive mapped type (as coined in ms/TS#47109) where you make a mapped type for each value K in Currency corresponding to just the values you want that begin with that value, and then use an indexed access type to turn that into the union you want:

type CurrencyQuote = { [K in Currency]: `${K}x${Exclude<Currency, K>}` }[Currency];
// type CurrencyQuote = "CADxUSD" | "CADxEUR" | "USDxCAD" |
//   "USDxEUR" | "EURxCAD" | "EURxUSD"

Looks good. Note how the Exclude<T, U> utility type is used to produce "everything in Currency that is not K" and avoid the duplication.

Playground link to code

CodePudding user response:

Alternatively, create the union of dupes by distributing them then exclude those explicitly:

type Dupes = Currency extends infer C extends string ? C extends C ? `${C}x${C}` : never : never;

type CurrencyQuote = Exclude<`${Currency}x${Currency}`, Dupes>;

Playground

  • Related