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>
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.
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>;