Add TypeScript To An Existing React Project
Learn how to add TypeScript to your existing React project in a few simple steps.
When you're declaring an array type in TypeScript, you've got one of two options: Array<T>
or T[]
.
Dominik (@TKDodo on Twitter), one of the maintainers of React Query, recently posted an article on which option you should choose.
He strongly advocated for Array<T>
, but I think the picture is a little more complex.
Array<T>
and T[]
are functionally identical in your code.ts
constfirstTest = (arr :Array <string>) => {};constsecondTest = (arr : string[]) => {};// Both behave the same!firstTest (["hello", "world"]);secondTest (["hello", "world"]);
keyof
with T[]
can lead to unexpected results.ts
typePerson = {id : string;name : string;};constType 'string[]' is not assignable to type 'keyof Person[]'.2322Type 'string[]' is not assignable to type 'keyof Person[]'.: keyof result Person [] = ["id", "name"];
The fix is to use Array<T>
instead:
ts
constresult :Array <keyofPerson > = ["id", "name"];
Dominik argues that Array<string>
is more readable than string[]
. This is subjective - it's like the difference between reading "array of strings" or "string array".
When hovering values or displaying errors, TypeScript uses the T[]
syntax. Inexperienced TS devs might experience cognitive load when translating between Array<T>
in their code and T[]
in their errors.
ts
constarray = [1, 2];
Overall, I disagree with Dominik that Array<T>
is always the better choice. There are enough caveats to either approach that I won't be making a recommendation one way or the other.
But - you should be consistent. You can use this ESLint rule to enforce one or the other in your codebase. And if I had to choose, I would choose T[]
.
Developers love a syntactical argument - especially when there is little functional difference between the two options.
Array<T>
and T[]
behave exactly the same, as noted above - with one small exception.
ts
typeTest1 = [...Array <string>, ...Array <string>];typeA rest element cannot follow another rest element.1265A rest element cannot follow another rest element.Test2 = [...string[], ...string[]];
Here, we're getting an error using the T[]
syntax when trying to use it in a rest position. But even this behavior might disappear in a future TS version, as this PR demonstrates.
So, we can treat the two as functionally the same.
keyof
If you're going to make a firm judgment on which syntax to use, you need to consider the keyof
operator.
As described above, keyof
with T[]
can lead to unexpected results.
ts
typePerson = {id : string;name : string;};constType 'string[]' is not assignable to type 'keyof Person[]'.2322Type 'string[]' is not assignable to type 'keyof Person[]'.: keyof result Person [] = ["id", "name"];
You would think here that keyof Person
would resolve before the []
operator kicks in, meaning you'd end up with a type like ('id' | 'name')[]
.
But unfortunately, the []
resolves first, so you end up performing a keyof
on Person[]
.
You can fix this by wrapping keyof Person
in parentheses:
ts
constresult : (keyofPerson )[] = ["id", "name"];
Or, you can use Array<T>
instead:
ts
constresult :Array <keyofPerson > =["id", "name"];
Dominik argues that Array<T>
is more readable than T[]
. You might agree with this - but I would argue that it's subjective.
I don't want to offer an opinion here - but I want to make sure your opinion is well-informed.
If you want to stay consistent with Array<T>
, you'll probably also want to use the ReadonlyArray<T>
type:
ts
constarray :ReadonlyArray <string> = ["hello", "world"];Property 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.array .("foo"); push
We can compare this to the readonly T[]
syntax:
ts
constarray2 : readonly string[] = ["hello", "world"];Property 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.array2 .("foo"); push
Which do you prefer? I find this one pretty hard to differentiate.
For handling arrays of arrays, you'll also want to consider Array<Array<T>>
:
ts
constarray :Array <Array <string>> = [["hello", "world"],["foo", "bar"],];
We can compare it to the T[][]
syntax:
ts
constarray2 : string[][] = [["hello", "world"],["foo", "bar"],];
Which do you prefer?
T[]
TypeScript does offer an opinion on which it prefers. In hovers and errors, TypeScript will always use the T[]
syntax.
ts
constarray = [1, 2];constasConstArray = [1, 2] asconst ;constarrayOfArrays = [[1, 2],[3, 4],];conststringArray = ["hello", "world"];constType 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.2322Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.: number[] = numArray stringArray ;
This means that if you're using Array<T>
in your code, less experienced TypeScript developers will experience some cognitive load translating between the two syntaxes.
This is a big reason why, to me at least, T[]
feels more natural - it's more present in the language, encouraged by the compiler, and used everywhere in the docs.
After all of this deep thought, I don't think there's a clear winner here.
I have personally lost hours of my life trying to figure out why keyof T[]
wasn't working as expected.
But I can also see a strong argument that T[]
is the more intuitive choice because of how embedded it is within TypeScript as a whole.
It really comes down to one question: would I reject a PR containing the one we didn't use? No.
Use whichever you like. Use a linter to stay consistent. And don't worry about it too much.
Share this article with your friends
Learn how to add TypeScript to your existing React project in a few simple steps.
Learn the essential TypeScript configuration options and create a concise tsconfig.json file for your projects with this helpful cheatsheet.
Big projects like Svelte and Drizzle are not abandoning TypeScript, despite some recent claims.
Learn different ways to pass a component as a prop in React: passing JSX, using React.ComponentType, and using React.ElementType.
Learn about TypeScript performance and how it affects code type-checking speed, autocomplete, and build times in your editor.
When typing React props in a TypeScript app, using interfaces is recommended, especially when dealing with complex intersections of props.