Reading TypeScript Errors
Read TypeScript's error messages: the error number, the chain of reasons read from the bottom up, the common errors and fixes, and where the red line lands.
- 01Read one errorFind where it is, what it says you gave and what the place needs, and what its number means.
- 02Read a long errorStart at the last line of a chain to find the real mismatch, and use the lines above it as the path.
- 03Find the real mistakeFix the common errors, and move an error to its cause with an annotation, a split line, or a named type.
What a type error tells you
A signup form asks for an email and an age. The age arrives from a text box, so it's still text. TypeScript marks it, on purpose here:
type SignupForm = { email: string; age: number }; const form: SignupForm = { email: "asha@example.com", age: "30",};console.log(form.age);Line 1 says a SignupForm has an email that is a string (text) and an age that is a number. Lines 3 to 6 make a form with that type. Line 5 gives the age the text "30", and that's the line TypeScript marks. Line 7 still prints 30 when the code runs, because a type error doesn't stop the code.
Look at the list under the code. The error has three parts:
- Where:
Line 5. In the code, a red wavy line, the squiggle, sits underage. - What:
Type 'string' is not assignable to type 'number'."Assignable" means "allowed to be stored there". So it says: you gave astring, and this place needs anumber. - Which number:
ts(2322). Every kind of TypeScript error has a number.
"30". The second is what age needs.Many errors are longer. Under the first line they add more lines, each pushed a little to the right. Each of those lines says why the line above it is true. This lesson shows you how to read them, which errors you'll see most, and what to do when the red line isn't where the mistake is.
You post a parcel, and it comes back with a note. The first line says "Parcel returned". That's true, but it doesn't help. The line under it says "Address not accepted". The last line says "No house number 12 on Oak Street". That's the line you act on: you fix the house number. The reference number at the top lets you ask the courier about it.
- In real life: The note's first line: "Parcel returned"
- In TypeScript: The first line:
Type 'ApiCart' is not assignable … - In real life: Each line under it says why: "Address not accepted"
- In TypeScript: Each indented line says why about the line above
- In real life: The last line: "No house number 12 on Oak Street"
- In TypeScript: The last line:
Type 'string' is not assignable to type 'number' - In real life: A reference number you can quote on the phone
- In TypeScript:
ts(2345), a number you can search for
A type error says where, what, and which number. The message reads "what you gave" first and "what the place needs" second. A long error is a chain of reasons: the first line is the summary, and the last line is usually the real mismatch. The red line shows where TypeScript noticed, which isn't always where the mistake is.
Where, what, and which number
Change the age yourself and watch the message. Type a new value for line 5. The list under the code updates as you type:
type SignupForm = { email: string; age: number }; const form: SignupForm = { email: "asha@example.com", age: "30",};console.log(form.age);"30"1 error: line 5ts(2322)prints 30Line 5 gives age the text "30". The message is Type 'string' is not assignable to type 'number': you gave a string, and age needs a number.
With 30 or Number("30"), the value is a number, so the error is gone. With true the message says Type 'boolean' is not assignable to type 'number', and with null it says Type 'null' …. The first type changes with what you type. The second stays number, because the place still needs one. The number stays 2322, because it's still the same kind of mistake. An empty box gets a different number, 1109, and Expression expected: the line isn't finished, so there's nothing to check yet.
Here's how to read each part of an error:
| The part | What it tells you | What to do with it |
|---|---|---|
Line 5 (or signup.ts:5:3) | Where TypeScript found the problem: the line, and in a terminal the column too. | Start looking here, but the mistake may be somewhere else. |
Type 'string' | The type of what you gave: here, the text "30". | Ask where that value came from. |
type 'number' | The type this place needs: here, the age: number in SignupForm. | Ask whether the value or the type is wrong. |
ts(2322) | The error's number. The words change with your names, but the number doesn't. | Search for TS2322 to find other people's answers. |
The order matters. "Assignable" goes one way: a value of the first type must fit where the second type is needed. A number fits where number | null is needed, but number | null doesn't fit where only a number goes. (Structural typing explains how TypeScript decides what fits.)
A terminal shows the same error with a little more. How TypeScript works showed how to read the first lines. Here is the signup form saved as signup.ts and checked with npx tsc --noEmit:
signup.ts:5:3 - error TS2322: Type 'string' is not assignable to type 'number'.
5 age: "30",
~~~
signup.ts:1:36
1 type SignupForm = { email: string; age: number };
~~~
The expected type comes from property 'age' which is declared here on type 'SignupForm'Line 1 has the place, signup.ts:5:3 (file, line 5, column 3), then TS2322 and the message. Lines 3 and 4 show line 5 of the file with ~~~ under age. Lines 6 to 9 are a related note: it points at line 1 of the file and says The expected type comes from property 'age' which is declared here on type 'SignupForm'. That tells you where the number came from. If the type is the thing that's wrong, that's the line to change. Editors like VS Code list the same note under the error in their list of problems.
Long errors: read from the bottom up
A cart comes from a server. Its prices arrive as text, and showTotal wants a Cart with number prices. The error is six lines long, on purpose:
type Item = { name: string; price: number };type Cart = { items: Item[]; total: number };type ApiCart = { items: { name: string; price: string }[]; total: number }; function showTotal(cart: Cart) { console.log(`Total: $${cart.total}`);} const fromServer: ApiCart = { items: [{ name: "Tea", price: "3" }], total: 3 };showTotal(fromServer);Line 1 says an Item has a number price. Line 2 says a Cart has a list of items and a total. Line 3 describes what the server sends, an ApiCart, where each price is a string. Lines 5 to 7 are showTotal, which takes a Cart. Line 9 makes the server's cart. Line 10 passes it to showTotal, and TypeScript marks the argument fromServer. It prints Total: $3 when it runs.
Here is the whole message, as the list under the code shows it:
Argument of type 'ApiCart' is not assignable to parameter of type 'Cart'.
Types of property 'items' are incompatible.
Type '{ name: string; price: string; }[]' is not assignable to type 'Item[]'.
Type '{ name: string; price: string; }' is not assignable to type 'Item'.
Types of property 'price' are incompatible.
Type 'string' is not assignable to type 'number'.The first line is the summary: the ApiCart doesn't fit where a Cart is needed. Each line under it is pushed further right, and answers "why?" about the line above. The TypeScript handbook calls these lines elaborations. Read them from the bottom up:
- Line 6, the last:
Type 'string' is not assignable to type 'number'. This is the real mismatch: text where a number goes. - Line 5:
Types of property 'price' are incompatible. The text is aprice. - Lines 4 and 3: the price belongs to one item, and that item is in an array of items.
- Line 2:
Types of property 'items' are incompatible. The array is the cart'sitems.
So the path is items, then one item, then price, and the problem is that it's text. Two fixes make sense: change ApiCart if the server really sends numbers, or turn each price into a number before calling showTotal.
showTotal(fromServer), one card per reason. Each card answers "why?" about the card above it. The last card is the real mismatch: a price that is text.Sometimes TypeScript shortens the path into one line. Break a server reply in different places and watch the middle line:
type Price = { amount: number; currency: string };type Product = { name: string; price: Price };type CartLine = { product: Product; quantity: number }; const reply = { product: { name: "Tea", price: { amount: "3", currency: "USD" } }, quantity: 2,};const line: CartLine = reply;product.price.amount1 error: line 9Type 'string' is not assignable to type 'number'.The first line names the whole reply and CartLine. The middle line gives the path: The types of 'product.price.amount' are incompatible between these types. The last line says why: Type 'string' is not assignable to type 'number'. The amount "3" is text.
With Amount as text, the middle line is The types of 'product.price.amount' are incompatible between these types: the whole path, written like the code you'd use to reach the value. Currency missing stops the path at product.price, and its last line is a different error, Property 'currency' is missing …. Whatever the chain says above, the last line tells you what to fix.
In the playground, the error lands on line 9, under the variable line, because reply was made first and stored later. Write the same object straight into the typed variable, and TypeScript can point at the exact property:
type Price = { amount: number; currency: string };type Product = { name: string; price: Price };type CartLine = { product: Product; quantity: number }; const line: CartLine = { product: { name: "Tea", price: { amount: "3", currency: "USD" } }, quantity: 2,};Line 5 says the object is a CartLine from the start. The squiggle is now under amount on line 6, and the message is one line: Type 'string' is not assignable to type 'number'. When TypeScript can mark the exact spot, it doesn't need a chain.
One error with two chains
Some functions can be called in more than one way. Each way is called an overload (Function overloads explains them). When no way fits, you get error 2769, with one chain per overload. Here a click listener expects a key, on purpose:
document.addEventListener("click", (event: KeyboardEvent) => { console.log(event.key);});Line 1 listens for "click" and says the event is a KeyboardEvent. Line 2 reads its key. The error is long. Point at the red line to read it. It has this shape:
No overload matches this call.The summary.Overload 1 of 2, '(type: "click", …)', gave the following error.Then its chain, ending withType 'PointerEvent' is missing the following properties from type 'KeyboardEvent': …Overload 2 of 2, '(type: string, …)', gave the following error.Then a second chain about a plainEvent.
Read the chain for the overload you meant. You wrote "click", so overload 1 is yours, and its last line says a click gives a PointerEvent, which has no key. Overload 2 is the general one for any event name; it's rarely the one you meant. If you wanted keys, listen for "keydown":
document.addEventListener("keydown", (event: KeyboardEvent) => { console.log(event.key);});Line 1 now listens for "keydown", which gives a KeyboardEvent, so there are no errors. (Typing events shows which event each name gives.)
The errors you'll meet most
A few error numbers make up most of what you'll see. In the table, X, Y, T, and p stand for the types and names from your code:
| Number | The message | What it usually means |
|---|---|---|
| 2322 | Type 'X' is not assignable to type 'Y'. | A value doesn't fit the type of the place you put it. |
| 2345 | Argument of type 'X' is not assignable to parameter of type 'Y'. | A value you pass to a function doesn't fit the parameter. |
| 2339 (2551) | Property 'p' does not exist on type 'T'. | The type has no property with that name. 2551 adds Did you mean …? for a near miss. |
| 18047, 18048 | 'cart' is possibly 'null'. or … 'undefined'. | The value may be missing. Check it before you use it. |
| 2531, 2532 | Object is possibly 'null'. or … 'undefined'. | The same, for a value with no simple name, like document.querySelector("input"). |
| 7006 | Parameter 'p' implicitly has an 'any' type. | A parameter needs a type, and TypeScript can't work one out. |
| 2741 | Property 'p' is missing in type 'X' but required in type 'Y'. | An object has fewer properties than its type needs. |
| 2353 | Object literal may only specify known properties, and 'p' does not exist in type 'T'. | An object written in place has a property its type doesn't list. |
| 2769 | No overload matches this call. | None of the function's ways to be called fits. Each one gets its own chain. |
Pick a number to see a small example, then switch to the fixed version:
let total: number = 0;total = "7";console.log(total);ts(2322)1 error: line 2prints 7Type 'string' is not assignable to type 'number': line 2 stores text in total, which line 1 made a number. The fix stores the number 7.
A few things the examples show:
- 2322 and 2345 are the same check in two places: storing a value, and passing it to a function. The second one says
Argumentandparameter. - 2741 and 2353 are opposites: a property the type needs is missing, or an extra one isn't in the type. 2353 only applies to an object written in place (Excess property checks explains why).
- 18048 says
'cake' is possibly 'undefined'. With a value that may benull, it's 18047. When the value has no simple name, likedocument.querySelector("input").value, the message saysObject is possibly 'null'instead (2531, or 2532 forundefined). null and undefined covers the fixes. - 7006 only appears when
noImplicitAnyis on, andstrictturns it on. You get one error per parameter.
Where the red line lands
TypeScript marks the place where it finds that something doesn't fit. That's often not where you made the mistake. Here a function turns a name and an address into a user, with a typo:
type User = { name: string; email: string }; function toUser(name: string, address: string) { return { name, emial: address };} const asha: User = toUser("Asha", "asha@example.com");console.log(asha.email);undefined1 error: line 7The typo is on line 4, but the red line is on line 7: Property 'email' is missing in type '{ name: string; emial: string; }' but required in type 'User'. toUser may return any object, so TypeScript only finds a problem where its result has to be a User.
At step 1, line 4 spells emial, but line 4 is fine as far as TypeScript knows: toUser has no return type, so any object can come back. The first place the result must be a User is line 7, so that's where the error is. It even says Property 'email' is missing, which is true but sends you looking in the wrong place.
At step 2, line 3 says : User, the type the function should return. Now TypeScript checks the object on line 4 against User, and the error moves there. It says Did you mean to write 'email'?. Writing the type you meant moves the error to the line that breaks it. (Annotations and inference covers return types.)
User. With : User on line 3, it marks line 4, the typo itself.When the same error shows up in several places, look for what they share:
type CartItem = { name: string; price: number }; function makeItem(name: string, price: string) { return { name, price };} const cart: CartItem[] = [makeItem("Tea", "3"), makeItem("Cake", "4")];console.log(cart.length);Line 3 says makeItem takes a price that is a string. Line 4 returns it. Line 7 calls makeItem twice, and both calls get the same error, ending in Type 'string' is not assignable to type 'number'. You could fix both calls, but the mistake is the one parameter type on line 3. Change it to price: number and pass 3 and 4, and both errors go. Line 8 prints 2 either way.
Investigate with your editor
Your editor knows every type in your code. When an error confuses you, ask it. Point at a name to see its type. In this lesson, a // ^? line asks the same question and writes the answer after it. Here is a long line with two errors in it:
const prices = new Map([["tea", 3], ["cake", 4]]);const cart = ["tea", "cake"];const total = cart.map((id) => prices.get(id)).reduce((sum, price) => sum + price, 0);console.log(total);Line 1 makes a Map from product ids to prices. Line 2 is the cart, a list of ids. Line 3 turns each id into its price with map, then adds them up with reduce (map, filter, and reduce explains both). TypeScript marks sum and price on line 3: 'sum' is possibly 'undefined', and the same for price. But where does the undefined come from? Split the line into steps, and ask about the first one:
const prices = new Map([["tea", 3], ["cake", 4]]);const cart = ["tea", "cake"];const linePrices = cart.map((id) => prices.get(id));// ^?const total = linePrices.reduce((sum, price) => sum + price, 0);console.log(total);Line 3 is the first step, with its own name, linePrices. The query on line 4 answers const linePrices: (number | undefined)[]: an array where each item is a number or undefined. So the undefined comes from prices.get(id). A Map gives back undefined for an id it doesn't have. The errors are now on line 5, where the sum uses those values. Give a missing price a value with ?? 0:
const prices = new Map([["tea", 3], ["cake", 4]]);const cart = ["tea", "cake"];const linePrices = cart.map((id) => prices.get(id) ?? 0);// ^?const total = linePrices.reduce((sum, price) => sum + price, 0);// ^?console.log(total);Line 3 adds ?? 0, which gives 0 when the left side is undefined (Nullish coalescing and optional chaining). The query on line 4 now answers const linePrices: number[], and the one on line 6 answers const total: number. No errors, and line 7 prints 7.
A starting value can cause errors too. An empty object {} has the type {}: an object with no properties.
type CartItem = { name: string; price: number };const cart: CartItem[] = [{ name: "Tea", price: 3 }, { name: "Cake", price: 4 }];const byName = cart.reduce((totals, item) => { totals[item.name] = item.price; return totals;}, {});console.log(byName);Line 3 builds an object with reduce, starting from {} on line 6. Line 4 stores each price under its item's name, and TypeScript marks it: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. Read the end first: the type is {}, which has no room for names. The fix is to name the type you mean and give it to the starting value:
type CartItem = { name: string; price: number };const cart: CartItem[] = [{ name: "Tea", price: 3 }, { name: "Cake", price: 4 }];type PriceList = Record<string, number>;const start: PriceList = {};const byName = cart.reduce((totals, item) => { totals[item.name] = item.price; return totals;}, start);console.log(byName);Line 3 names the type PriceList: an object whose keys are strings and whose values are numbers (Utility types covers Record). Line 4 makes the empty start with that type, and line 8 passes it in. reduce now knows what it's building, so there are no errors, and line 9 prints { Tea: 3, Cake: 4 }. Point at byName to see const byName: PriceList.
These moves work on most confusing errors:
| When | Try | What it shows |
|---|---|---|
| You don't know what type a name has | Point at it, or add a // ^? line under it. | The type TypeScript worked out for it. |
| A long line has an error deep inside | Split it into steps, each with its own name. | The first step whose type isn't what you expected. |
| The error is far from where you think the mistake is | Write the type you meant: a return type, or a variable's type. | The error moves to the line that breaks your type. |
An empty {} or [] gets the wrong type | Give the starting value a named type. | Errors about the real data, not about {}. |
| The same error appears in many places | Look for what those places share. | One cause, like a parameter typed wrong. |
A theme from the address bar
A settings page reads the theme from the page's address, like ?theme=dark, with URLSearchParams. Fix it the way you would in a real project: read the last line, fix that, and read again.
type Theme = "light" | "dark"; function setTheme(theme: Theme) { console.log(`Theme: ${theme}`);} const params = new URLSearchParams("?theme=dark");setTheme(params.get("theme"));Theme: dark1 error: line 8Argument of type 'string | null' is not assignable to parameter of type 'Theme'. The last line gives one reason: Type 'null' is not assignable to type 'Theme'. get gives back null when the address has no theme.
Line 1 says a Theme is "light" or "dark", and lines 3 to 5 set it. Line 7 reads the address. At step 1, line 8 passes params.get("theme") straight in. Its type is string | null, and the error's last line names only null. At step 2, the if on line 9 removes null, and a second error appears on line 10 about string. It was there all along. TypeScript stops at the first reason it finds, so fixing one reason can show the next. Step 3 checks for the two allowed values (Narrowing explains how the if changes the type), and the errors are gone.
Now practise reading the number. Sort each line by the error TypeScript gives it:
const quantity: number = "2";let total = 0; total = "7";Math.round("4.5");parseInt(3);const user = { name: "Asha" }; user.email;[3, 4].total;const tea: { name: string; price: number } = { name: "Tea" };const user: { name: string; email: string } = { name: "Asha" };
Each line has one error. Put it under the error number TypeScript reports.
Common mix-ups
- "The first line of an error is the important one." No. It's a summary. The last line of the chain usually names the real mismatch.
- "The red line is where the mistake is." No. It's where TypeScript noticed. Writing the type you meant, like a return type, moves the error closer to the cause.
- "In 'X is not assignable to Y', X is what the place needs." No. X is what you gave. Y is what the place needs.
- "Fixing what the last line says fixes the error." Not always. It names the first reason TypeScript found. Another reason, like the
stringin the theme, can appear once it's fixed. - "An error means the code won't run." No. The code still runs with the types removed. The signup form prints
30, and the cart printsTotal: $3. - "Adding
anyfixes an error." It hides it. The value is just as wrong when the code runs.
Practice exercises
Each exercise is TypeScript. Press Edit & run on the starter code to try a fix: the errors update as you type, and you can point at a name to see its type. Guess first, then use the hints.
Without running it: which error number will TypeScript report for this code?
function addToCart(name: string, quantity: number) {
console.log(`${quantity} x ${name}`);
}
addToCart("Tea", "2");function addToCart(name: string, quantity: number) { console.log(`${quantity} x ${name}`);} addToCart("Tea", 2);Line 5 passes the text "2" to the quantity parameter, which needs a number, so TypeScript reports 2345: Argument of type 'string' is not assignable to parameter of type 'number'. Passing 2 fixes it, and it prints 2 x Tea.
Read the error on line 10. Which property has the wrong type? Then fix the code.
type Address = { street: string; zip: string };type Customer = { name: string; address: Address };type ApiCustomer = { name: string; address: { street: string; zip: number } }; function shipTo(customer: Customer) { console.log(`${customer.name}, ${customer.address.zip}`);} const fromServer: ApiCustomer = { name: "Asha", address: { street: "Oak Street 12", zip: 56001 } };shipTo(fromServer);type Address = { street: string; zip: string };type Customer = { name: string; address: Address }; function shipTo(customer: Customer) { console.log(`${customer.name}, ${customer.address.zip}`);} const fromServer: Customer = { name: "Asha", address: { street: "Oak Street 12", zip: "56001" } };shipTo(fromServer);The error says The types of 'address.zip' are incompatible between these types, then Type 'number' is not assignable to type 'string'. The zip code arrives as a number. This fix types the reply as a Customer and sends the zip as text, so it prints Asha, 56001.
Which property is the profile missing? Add it so the error goes away.
type Profile = { name: string; email: string; newsletter: boolean }; const asha: Profile = { name: "Asha", email: "asha@example.com",};console.log(asha.newsletter ? "Subscribed" : "Not subscribed");type Profile = { name: string; email: string; newsletter: boolean }; const asha: Profile = { name: "Asha", email: "asha@example.com", newsletter: true,};console.log(asha.newsletter ? "Subscribed" : "Not subscribed");Property 'newsletter' is missing in type '{ name: string; email: string; }' but required in type 'Profile'. Adding newsletter: true gives the object everything a Profile needs, and it prints Subscribed.
TypeScript marks line 7, but the mistake is somewhere else. Add a return type to toProduct. Which line does TypeScript mark now? Then fix it.
type Product = { name: string; price: number }; function toProduct(name: string, cents: number) { return { name, prise: cents / 100 };} const tea: Product = toProduct("Tea", 300);console.log(tea.price);type Product = { name: string; price: number }; function toProduct(name: string, cents: number): Product { return { name, price: cents / 100 };} const tea: Product = toProduct("Tea", 300);console.log(tea.price);With : Product on line 3, the error moves to line 4: Object literal may only specify known properties, but 'prise' does not exist in type 'Product'. Did you mean to write 'price'? Fixing the spelling removes it, and the code prints 3.
What does this code print when it runs? Then split line 3 into two steps, find where the undefined comes from, and fix it.
const stock = new Map([["tea", 5], ["cake", 2]]);const order = ["tea", "cake", "scone"];const left = order.map((id) => stock.get(id)).reduce((sum, count) => sum + count, 0);console.log(left);const stock = new Map([["tea", 5], ["cake", 2]]);const order = ["tea", "cake", "scone"];const counts = order.map((id) => stock.get(id) ?? 0);// ^?const left = counts.reduce((sum, count) => sum + count, 0);console.log(left);The starter prints NaN: there's no scone, so one count is undefined, and 7 + undefined is NaN. Splitting the line shows counts would be (number | undefined)[]. With ?? 0, the query shows const counts: number[], and the code prints 7.
A search box should react to the Enter key. Read the error on line 2. What type of event does a click give? Then fix the code.
const search = document.createElement("input");search.addEventListener("click", (event: KeyboardEvent) => { if (event.key === "Enter") console.log("Search");});const search = document.createElement("input");search.addEventListener("keydown", (event: KeyboardEvent) => { if (event.key === "Enter") console.log("Search");});Overload 1's last line is Type 'PointerEvent' is missing the following properties from type 'KeyboardEvent': …. A click gives a PointerEvent, which has no key. Listening for "keydown" gives a KeyboardEvent, and the error is gone.
Check your understanding
The quiz shows its code without the checker, so the error list doesn't give the answers away. Read each error the way the lesson does: what you gave, what the place needs, and the last line of the chain.
Question 1 of 7In
Type 'string' is not assignable to type 'number', which type does the place need?Choose an answer to see the explanation.
Question 2 of 7Which property in this long error has the wrong type?
Read the code, then predictTextArgument of type 'ApiOrder' is not assignable to parameter of type 'Order'. Types of property 'items' are incompatible. Type '{ name: string; quantity: string; }[]' is not assignable to type 'OrderItem[]'. Type '{ name: string; quantity: string; }' is not assignable to type 'OrderItem'. Types of property 'quantity' are incompatible. Type 'string' is not assignable to type 'number'.Choose an answer to see the explanation.
Question 3 of 7Which error number does this cart item get?
Read the code, then predicttype CartItem = { name: string; price: number }; const tea: CartItem = { name: "Tea", price: 3, size: "large" };Choose an answer to see the explanation.
Question 4 of 7Which line does TypeScript mark in this order code?
Read the code, then predicttype Order = { id: number; total: number }; function makeOrder(id: number, amount: number) { return { id, totl: amount }; } const order: Order = makeOrder(7, 12);Choose an answer to see the explanation.
Question 5 of 7What does
'cake' is possibly 'undefined'ask you to do?Choose an answer to see the explanation.
Question 6 of 7An error starts with
No overload matches this call. What should you read first?Choose an answer to see the explanation.
Question 7 of 7A long line with
mapandreducehas an error deep inside. What helps you find where the type went wrong?Choose an answer to see the explanation.
Key takeaways
- An error has a place, a message, and a number like
ts(2322). Search for the number asTS2322. - In
Type 'X' is not assignable to type 'Y', X is what you gave and Y is what the place needs. - A long error is a chain: each line says why about the one above. Start at the last line, and use the lines above as the path.
- For "No overload matches this call", read the chain for the way of calling you meant.
- The red line is where TypeScript noticed. Write the type you meant, like a return type, to move it to the mistake.
- Point at names, add
^?queries, split long lines, and name types to see where a type went wrong. - Fixing the last line can reveal the next reason. Read again after each fix.
Remember the one-liner.
Read what you gave and what was needed, start a long error from its last line, and move the error to the mistake by writing the type you meant.
Coming next: Habits of good TypeScript. It collects the habits that keep errors like these rare and easy to read: letting inference work, avoiding any, and modelling states with unions.