With TypeScript v3.4 const assertions:
export const AVAILABLE_STUFF = <const> ['something', 'else'];
export type Stuff = typeof AVAILABLE_STUFF[number];
Answer from Archit Garg on Stack OverflowIs it possible to create a typescript type from an array? - Stack Overflow
Array<Type> VS Type[] in Typescript - Stack Overflow
Defining array with multiple types in TypeScript - Stack Overflow
Best way to declare an array type in TypeScript? - Stack Overflow
Videos
With TypeScript v3.4 const assertions:
export const AVAILABLE_STUFF = <const> ['something', 'else'];
export type Stuff = typeof AVAILABLE_STUFF[number];
One built-in option would be to use an enum instead of the type and array approach.
export enum Stuff {
something = 'something',
else = 'else',
}
export const AVAILABLE_STUFF: Stuff[] = Object.values(Stuff);
Another option is to extract the type from the type of AVAILABLE_STUFF. To do this we must force the compiler to infer a tuple of string literals for AVAILABLE_STUFF. This can be done in 3.4 with as const or before 3.4 using an extra function. After AVAILABLE_STUFF is a tuple type we can just use a type query to get the type of the elements:
export const AVAILABLE_STUFF = (<T extends string[]>(...o: T)=> o)('something', 'else'); // typed as ["something", "else"]
// export const AVAILABLE_STUFF = ['something', 'else'] as const; // typed as ["something", "else"] in 3.4
export type Stuff = typeof AVAILABLE_STUFF[number] //"something" | "else"
A few explanations of the above code. typeof AVAILABLE_STUFF gives us the type of the constant (["something", "else"]) to get the [number] is called a type query and will give us the type of an item in the tuple.
The (<T extends string[]>(...o: T)=> o) is just an IIFE we use to force the compiler to infer a string literal tuple type. It has to be generic as the compiler will only infer literal types and tuples in certain cases (a type parameter with a constraint of string being one of them). The as const version is what I would recommend using when it becomes available as it is more readable.
There isn't any semantic difference
There is no difference at all. Type[] is the shorthand syntax for an array of Type. Array<Type> is the generic syntax. They are completely equivalent.
The handbook provides an example here. It is equivalent to write:
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
Or:
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg;
}
And here is a quote from some release notes:
Specifically,
number[]is a shorthand version ofArray<number>, just asDate[]is a shorthand forArray<Date>.
About the readonly type modifier
TypeScript 3.4, introduces the readonly type modifier. With a precision:
the
readonlytype modifier can only be used for syntax on array types and tuple types
let err2: readonly Array<boolean>; // error!
let okay: readonly boolean[]; // works fine
The following declaration is equivalent to readonly boolean[]:
let okay2: ReadonlyArray<boolean>;
There is a difference when you are defining fixed length arrays. You can't define a fixed length array with Array<>, you have to use the shorthand syntax:
const myFunc1 = (arg: [string, number, boolean]) => {
console.log(arg);
};
myFunc1(["hello world", 123, true]);
// error: TS2314: Generic type 'Array ' requires 1 type argument(s).
const myFunc2 = (arg: Array<string, number, boolean>) => {
console.log(arg);
};
myFunc2(["hello world", 123, true])
Defining array with multiple types in TypeScript
Use a union type (string|number)[] demo:
const foo: (string|number)[] = [ 1, "message" ];
I have an array of the form: [ 1, "message" ].
If you are sure that there are always only two elements [number, string] then you can declare it as a tuple:
const foo: [number, string] = [ 1, "message" ];
And you can even provide meaningful names for the tuple members e.g. id and text:
const foo: [id: number, text: string] = [ 1, "message" ];
If you're treating it as a tuple (see section 3.3.3 of the language spec), then:
var t:[number, string] = [1, "message"]
or
interface NumberStringTuple extends Array<string|number>{0:number; 1:string}
var t:NumberStringTuple = [1, "message"];
TypeScript 3.4+
TypeScript version 3.4 has introduced so-called **const contexts**, which is a way to declare a tuple type as immutable and get the narrow literal type directly (without the need to call a function like shown below in the 3.0 solution).With this new syntax, we get this nice concise solution:
const furniture = ['chair', 'table', 'lamp'] as const;
type Furniture = typeof furniture[number];
More about the new const contexts is found in this PR as well as in the release notes.
TypeScript 3.0+
With the use of generic rest parameters, there is a way to correctly infer string[] as a literal tuple type and then get the union type of the literals.
It goes like this:
const tuple = <T extends string[]>(...args: T) => args;
const furniture = tuple('chair', 'table', 'lamp');
type Furniture = typeof furniture[number];
More about generic rest parameters
This answer is out of date; see @ggradnig's answer.
The best available workaround:
const furnitureObj = { chair: 1, table: 1, lamp: 1 };
type Furniture = keyof typeof furnitureObj;
const furniture = Object.keys(furnitureObj) as Furniture[];
Ideally we could do this:
const furniture = ['chair', 'table', 'lamp'];
type Furniture = typeof furniture[number];
Unfortunately, today furniture is inferred as string[], which means Furniture is now also a string.
We can enforce the typing as a literal with a manual annotation, but it brings back the duplication:
const furniture = ["chair", "table", "lamp"] as ["chair", "table", "lamp"];
type Furniture = typeof furniture[number];
TypeScript issue #10195 tracks the ability to hint to TypeScript that the list should be inferred as a static tuple and not string[], so maybe in the future this will be possible.