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
)