Reputation: 198188
If I have a type which looks like an array:
type names = ['Mike', 'Jeff', 'Ben'];
I can easily define another type which has values of items in names
:
type UserName = names[number]
For a function:
function hello(name: UserName) {
console.log(`Hello, ${name}!`)
}
I can only pass one of Mike
, Jeff
, Ben
to function hello
. If I give other values, like John
, it can't compile.
What if I don't have a type names
, but a const array names
?
const names = ['Mike', 'Jeff', 'Ben'];
type UserName = ???;
function hello(name: UserName) {
console.log(`Hello, ${name}!`)
}
hello('Mike');
Is it possible to define such a type UserName
?
Upvotes: 17
Views: 10434
Reputation: 21851
Again, one should most of the time:
exactly in that order.
Rarely one should do the opposite thing.
If you really need it though, you can do it like this:
const names = ['Mike', 'Jeff', 'Ben'] as ['Mike', 'Jeff', 'Ben'];
type UserName = typeof names;
because you want a tuple type (['Mike', 'Jeff', 'Ben']
), but arrays are never inferred as tuples by default, but only as arrays (string[]
in this case). However I don't see much sense in doing above thing and again I am suggesting you to do the opposite, idiomatic thing:
type UserName = ['Mike', 'Jeff', 'Ben'];
// however the above type is absolutely static
// and I don't know if it can provide any benefit so maybe this is more correct:
type UserName = ('Mike' | 'Jeff' | 'Ben')[]
const names: UserName = ['Mike', 'Jeff', 'Ben'] // ok
Upvotes: 2
Reputation: 327754
In TypeScript 3.4, which should be released in March 2019 it will be possible to tell the compiler to infer the type of a tuple of string literals as a tuple of string literals, instead of as string[]
, by using the as const
syntax. It should look like this:
const names = ['Mike', 'Jeff', 'Ben'] as const; // TS3.4 syntax
type Names = typeof names; // type Names = readonly ['Mike', 'Jeff', 'Ben']
type UserName = Names[number]; // 'Mike' | 'Jeff' | 'Ben'
Until then (in TypeScript 3.0 through 3.3) you can get this effect by using a helper function which gives the compiler hints to infer a narrower type:
type Narrowable = string | number | boolean | undefined | null | void | {};
const tuple = <T extends Narrowable[]>(...t: T)=> t;
const names = tuple('Mike', 'Jeff', 'Ben');
type Names = typeof names; // type Names = ['Mike', 'Jeff', 'Ben']
type UserName = Names[number]; // 'Mike' | 'Jeff' | 'Ben'
(Note that in both cases you can skip the intermediate Names
type and just define type UserName = (typeof names)[number]
if you prefer)
Okay, hope that helps. Good luck!
Upvotes: 26