I have a string literal union type Animal:
type Animal = 'GOAT' | 'GIRAFFE' | 'SALMON' | 'TUNA'
I also have a type Fish which is a subset of Animal:
type Fish = Extract<Animal, 'SALMON' | 'TUNA'> // type Fish = "SALMON" | "TUNA"
Now I want to have an array of string containing the Fish, so that I can use that array in the logic (e.g. to use the includes() function).
I could just define the array separately:
const FISH: ReadonlyArray<Fish> = ['SALMON', 'TUNA']
but now I have to maintain the list of fish in two places.
What I want to know is: is it possible to use the values in the array to define which string literals to extract for the Fish type while also only allowing the array to contain Animal strings?
Something like:
const FISH: ReadonlyArray<SubsetOfStringLiterals<Animal>> = ['SALMON', 'TUNA'] // Would error if it contained a value not in Animal
type Fish = typeof FISH[number] // type Fish = "SALMON" | "TUNA"
where SubsetOfStringLiterals would essentially be a utility like Partial but for string literal union types.
If you want to apply a constraint to a type and also infer something more specific, then you need to either use a generic function or the new satisfies operator.
Coming in the next Typescript version (4.9, currently in beta) is the satisfies operator which works nicely here.
// fine
const FISH = ['SALMON', 'TUNA'] as const satisfies ReadonlyArray<Animal>
// error
const FISH_BAD = ['COFFEE'] as const satisfies ReadonlyArray<Animal>
See playground
Typescript 4.8 or below, you'll need to use a simple generic identity function.
function makeSubAnimals<T extends readonly Animal[]>(arr: T) { return arr }
const FISH = makeSubAnimals(['SALMON', 'TUNA'] as const) // fine
const FISH_BAD = makeSubAnimals(['COFFEE'] as const) // error
See Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With