Home > Software design >  How do I write a typescript template literal using a union twice without invalid permutations?
How do I write a typescript template literal using a union twice without invalid permutations?

Time:12-09

I currently am trying to sort out how to create a string literal type that maps table and fields ${Table}.${Field} as ${Field}, but I can't seem to get the typescript type right.

The documentation isn't abundantly clear on what happens when a union is used more than once.

It appears that TypeScript treats every substitution in a template literal type independent of each other, so if you specify the same substitution in a template literal, where the generic value is a union, it will consider mismatched substitutions for the same variable valid. Is there a way to prevent this so that if I want to use the same value from a union twice in the template, it uses the same value?

Typescript 4.9.4

type QualifiedField<Table extends string, Field extends string> = Field | `${Table}.${Field} as ${Table}_${Field}`;

let test: QualifiedField<'test', 'BIG' | 'small'>;
test = 'BIG'; // ✅ Wanted
test = 'small'; // ✅ Wanted
test = 'test.BIG as test_BIG'; // ✅ Wanted
test = 'test.BIG as test_small'; // ❌ I don't want this
test = 'test.small as test_small'; // ✅ Wanted
test = 'test.small as test_BIG'; // ❌ I don't want this
test = 'this fails'; // ✅ Expected error: Type '"this fails"' is not assignable to type...

CodePudding user response:

We can prevent the invalid permutations by distributing over Field in a conditional type where we store each constituent inside a new inferred type F. For each "iteration" over the Field union, the F type is guaranteed to be a non-union string literal type.

type QualifiedField<Table extends string, Field extends string> = 
  Field | (Field extends infer F extends string 
    ? `${Table}.${F} as ${Table}_${F}` 
    : never
  )

Playground

  • Related