# Common errors while working with TypeScript - Natalia
## 2.2. How to fix error "Type X is not assignable to type Y"
Understanding "Type X is not assignable to type Y"
- This error message appears when TypeScript's type checker detects a mismatch between the expected type and the actual type of a variable, parameter, or return value.
- The error is a signal that your code might not behave as expected at runtime, and TypeScript is helping you catch this issue early.
### 2.2.1. Mismatched Types
Attempting to assign a value of one type to a variable of another type.
```typescript
let jonSnowAge: number = "twenty-five";
// Error: Type 'string' is not assignable to type 'number'.
```
Interpret and Fix: Jon Snow's age should be a number, not a string.
```typescript
let jonSnowAge: number = 25; // Corrected
```
### 2.2.2. Object Shape Mismatch
The assigned object doesn't match the expected shape
```typescript
interface Character {
name: string;
house: string;
}
let tyrion: Character = {
name: "Tyrion Lannister",
title: "Hand of the Queen"
};
// Error: Object literal may only specify known properties,
// and 'title' does not exist in type 'Character'
```
Interpret and Fix: Jon Snow's age should be a number, not a string.
Depends on your design, it can either be fixed in one of the following ways:
```typescript
//1. Adjust Object
let tyrion: Character = {
name: "Tyrion Lannister",
house: "Lannister"
};
//2. Update Interface:
interface Character {
name: string;
house: string;
title?: string; // Make title an optional property
}
```
### 2.2.3. Function Type Mismatch
Mismatch between function parameters or return types and their declared types
```typescript
function getNumberOfDragons(): number {
return "Drogon, Rhaegal, Viserion";
}
// Error: Type 'string' is not assignable to type 'number'.
```
Interpret and Fix: Correct the function to return the number of dragons, not their names.
```typescript
function getNumberOfDragons(): number {
return 3; // Corrected
}
```
### 2.2.4. Array and Tuple Mismatch
Assigning an array or tuple to a type that expects a different structure or element types.
```typescript
let starkChildren: [string, string, string, string] =
["Robb", "Sansa", "Arya", 12];
// Error: Type 'number' is not assignable to type 'string'.
```
Interpret and Fix: Ensure all elements in the tuple are of the correct type, correcting Arya's age to her name.
```typescript
let starkChildren: [string, string, string, string] = ["Robb", "Sansa", "Arya", "Bran"]; // Corrected
```
### 2.2.5. Enum Mismatch
Assigning a value to an enum type that does not exist in the enum.
```typescript
enum House {
Stark,
Lannister,
Targaryen
}
let jonHouse: House = House['Baratheon'];
// Error: Property 'Baratheon' does not exist on type 'typeof House'.
```
Interpret and Fix: The House enum does not include `Baratheon`. Ensure you're assigning a value from the defined enum options.
```typescript
let jonHouse: House = House.Stark;
//or
let daenerysHouse: House = House["Targaryen"];
```
### 2.2.6. Union Type Mismatch
Assigning a value that doesn't fit any type in a union.
```typescript
type Weapon = 'Sword' | 'Dagger' | 'Bow';
let jonSnowWeapon: Weapon = 'Axe';
// Error: Type '"Axe"' is not assignable to type 'Weapon'.
```
Interpret and Fix: Ensure the value matches one of the union's types.
```typescript
let jonSnowWeapon: Weapon = 'Sword';
```
### 2.2.7. Incorrect Generic Types
Using a generic type that does not conform to the constraints or expected structure.
```typescript
interface Character {
name: string;
house: string;
}
function getCharacterDetail<T extends Character>(detail: T): string {
return detail.name;
}
const detail = getCharacterDetail({ name: "Arya", weapon: "Needle" });
// Error: Argument of type '{ name: string; weapon: string; }' is not assignable to parameter of type 'Character'.
```
Interpret and Fix: Adjust the input to match the Character interface or extend the interface to include additional properties.
There are a few ways to fix depend on your use cases.
- Mark house as optional:
```typescript
interface Character {
name: string;
house?: string; // Making house optional
weapon: string; // Adding weapon as an optional property
}
function getCharacterDetail<T extends Character>(detail: T): string {
return detail.name;
}
const detail = getCharacterDetail({ name: "Arya", weapon: "Needle" });
```
- Mark house as optional:
```typescript
interface Character {
name: string;
house: string;
weapon: string;
}
// Adjusting the function to accept a type that is part of Character but without enforcing 'house'
function getCharacterDetail<T extends Partial<Character> & Pick<Character, 'name' | 'weapon'>>(detail: T): string {
return detail.name;
}
const detail = getCharacterDetail({ name: "Arya", weapon: "Needle" });
```
- Overloading the Function:
```typescript
interface Character {
name: string;
house: string;
weapon: string;
}
// Overload signature for characters without a house
function getCharacterDetail(detail: Omit<Character, 'house'>): string;
// Original function signature
function getCharacterDetail(detail: Character): string;
// Function implementation
function getCharacterDetail(detail: any): string {
return detail.name;
}
const detail = getCharacterDetail({ name: "Arya", weapon: "Needle" });
```
## 2.3. How to fix error "expression of type string cannot be used to index"
**Challenge**
```typescript
const allegiances = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
// Initial attempt that causes TypeScript to complain
const getAllegiance = (character: string) => {
return allegiances[character];
// Error:expression of type 'string' can't be used to index type '{ "Jon Snow": string; "Tyrion Lannister": string; }'.
};
```
### 2.3.1. Define a Function with a Specific Key Type
```typescript
const allegiances = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
const getAllegianceByKey = (character: keyof typeof allegiances) => {
return allegiances[character]; // Correct
};
console.log(getAllegianceByKey("Jon Snow")); // "Stark"
```
### 2.3.2. Type Assertion within a Function
```typescript
const allegiances = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
const getAllegianceWithAssertion = (character: string) => {
return allegiances[character as keyof typeof allegiances]; // Correct
};
console.log(getAllegianceWithAssertion("Tyrion Lannister")); // "Lannister"
```
### 2.3.3. Type Guard to Ensure Key Validity
```typescript
const allegiances = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
const isKeyOfAllegiance = (key: any): key is keyof typeof allegiances => {
return key in allegiances;
}
const getAllegianceWithGuard = (character: string) => {
if (isKeyOfAllegiance(character)) {
return allegiances[character]; //Correct
}
return undefined; // Or handle the error as appropriate
};
console.log(getAllegianceWithGuard("Jon Snow")); // "Stark"
```
### 2.3.4. Index Signature for Flexible Object Indexing
```typescript
const allegiances = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
const allegiancesWithIndex: { [key: string]: string } = {
"Jon Snow": "Stark",
"Tyrion Lannister": "Lannister"
};
const getAllegianceWithIndex = (character: string) => {
return allegiancesWithIndex[character]; // Correct
};
console.log(getAllegianceWithIndex("Tyrion Lannister")); // "Lannister"
```
### Another example:
```typescript
const weapons = {
"Arya Stark": "Needle",
"Brienne of Tarth": "Oathkeeper"
};
const getWeapon = (hero: string) => {
// TypeScript error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ "Arya Stark": string; "Brienne of Tarth": string; }'.
return weapons[hero];
};
// Specific Key Type
const getAllegianceByKey = (character: keyof typeof allegiances) => allegiances[character];
// Type Assertion
const getAllegianceWithAssertion = (character: string) => allegiances[character as keyof typeof allegiances];
// Type Guard
function isCharacter(character: any): character is keyof typeof allegiances {
return character in allegiances;
}
const getAllegianceWithGuard = (character: string) => isCharacter(character) ? allegiances[character] : 'Unknown character';
// Index Signature
const allegiancesWithIndex: { [key: string]: string } = {
"Jon Snow": "Stark",
"Daenerys Targaryen": "Targaryen"
};
const getAllegianceWithIndex = (character: string) => allegiancesWithIndex[character];
```
## 2.4 How to handle variables that could be undefined
### Problem:
```typescript
interface Character {
name: string;
title?: string; // The title is optional, hence it could be undefined
}
const character: Character = {
name: "Arya Stark",
// title is not provided
};
const uppercaseTitle = (character: Character): string=> {
return character.title.toUpperCase(); //Error: 'character.title' is possibly 'undefined'.
}
```
### 2.4.1. Optional chaining
```typescript
interface Character {
name: string;
title?: string;
}
const arya: Character = {
name: "Arya Stark",
};
const uppercaseTitle = (character: Character): string | undefined =>{ //need to be the union string | undefined
return character.title?.toUpperCase();
}
```
### 2.4.2. Nullish Coalescing Operator (??) - for ES11
```typescript
interface Character {
name: string;
title?: string;
}
const arya: Character = {
name: "Arya Stark",
};
const uppercaseTitle = (character: Character): string =>{
return character.title?.toUpperCase() ?? "Title Unknown";
}
```
### 2.4.3. Type Guards
```typescript
interface Character {
name: string;
title?: string;
}
const arya: Character = {
name: "Arya Stark",
};
const uppercaseTitle = (character: Character): string =>{
if (character.title !== undefined) {
return character.title.toUpperCase();
} else {
return "Title Unknown";
}
}
```
### 2.4.4. Providing a Default Value
```typescript
interface Character {
name: string;
title?: string;
}
const arya: Character = {
name: "Arya Stark",
};
//logical OR (||) operator
const uppercaseTitle = (character: Character): string =>{
const title = character.title || "No Title";
return title.toUpperCase();
}
//or default parameter
const uppercaseTitle = (title: string = "Traveler"): string =>{
return title.toUpperCase();
}
uppercaseTitle(arya.title)
```
### 2.4.5. Type Assertion
only use it when you're absolutely sure 'title' is not undefined
```typescript
interface Character {
name: string;
title?: string;
}
const arya: Character = {
name: "Arya Stark",
title: "Princess"
};
const uppercaseTitle = (character: Character): string =>{
return character.title!.toUpperCase();
```
## 2.5 How to fix error "'value' implicitly has an 'any' type"
```typescript
const holdsIronThrone = (house) => { //Parameter 'house' implicitly has an 'any' type
const ironThroneHolder = "Targaryen";
return house === ironThroneHolder;
}
console.log(holdsIronThrone("Stark"));
```
### 2.5.1 Explicit Types or default value
```typescript
//Explicit Types
const holdsIronThrone = (house: string) => {
const ironThroneHolder = "Targaryen";
return house === ironThroneHolder;
}
console.log(holdsIronThrone("Stark")); // No error, returns false
//default value
const holdsIronThrone = (house = "Unknown") => {
const ironThroneHolder = "Targaryen";
return house === ironThroneHolder;
}
console.log(holdsIronThrone("Stark")); // No error, TypeScript infers house is a string from default value
```
### 2.5.2 Interfaces
```typescript
interface House {
name: string;
}
const holdsIronThrone = (house: House) => {
const ironThroneHolder: string = "Targaryen";
return house.name === ironThroneHolder;
}
console.log(holdsIronThrone({ name: "Stark" }));
```
### 2.5.3 Interfaces
```typescript
interface House {
name: string;
}
//Define an interface for the function overloads
interface holdsIronThrone{
(house: string): boolean;
(house: House): boolean;
// Single function implementation that handles all cases, assigned to a variable
const holdsIronThrone : holdsIronThrone= (house: any): boolean =>{
const ironThroneHolder = "Targaryen";
const houseName = typeof house === 'string' ? house : house.name;
return houseName === ironThroneHolder;
}
console.log(holdsIronThrone("Stark"));
console.log(holdsIronThrone({ name: "Targaryen" }));
```
## 2.6 How to fix error "Argument of type '..' is not assignable to parameter of type..."
```typescript
interface House {
name: string;
sigil: string;
}
const declareAllegiance = (house: House) => {
console.log(`${house.name} pledges loyalty to the realm.`);
};
declareAllegiance({ name: "Stark", sigil: "Direwolf", motto: "Winter is Coming" });
```
### 2.6.1 Simply structuring the value
```typescript
interface House {
name: string;
sigil: string;
}
const declareAllegiance = (house: House) => {
console.log(`${house.name} pledges loyalty to the realm.`);
};
declareAllegiance({ name: "Stark", sigil: "Direwolf"}); //remove motto to align with House interface
```
### 2.6.2 Type Assertion
Type assertions should be wielded with caution, for they tell TypeScript to trust the developer's knowledge over its own analysis.
```typescript
interface House {
name: string;
sigil: string;
}
const declareAllegiance = (house: House) => {
console.log(`${house.name} pledges loyalty to the realm.`);
};
declareAllegiance({ name: "Stark", sigil: "Direwolf", motto: "Winter is Coming" } as House);
```
## 2.7 How to fix error "Property Y does not exist on type X"
```typescript
interface Hero {
name: string;
house: string;
}
const jonSnow: Hero = {
name: "Jon Snow",
house: "Stark",
};
console.log(jonSnow.title); //Error: Property 'title' does not exist on type 'Hero'.
```
### 2.7.1 Simply define new interface
```typescript
interface HeroWithTitle {
name: string;
house: string;
title: string;
}
const jonSnow: HeroWithTitle = {
name: "Jon Snow",
house: "Stark",
title: "King in the North",
};
console.log(jonSnow.title);
```
### 2.7.2 Extending Interfaces
```typescript
interface Hero {
name: string;
house: string;
}
interface NobleHero extends Hero {
title: string;
}
const jonSnow: NobleHero = {
name: "Jon Snow",
house: "Stark",
title: "King in the North",
};
console.log(jonSnow.title);
```
### 2.7.3 Optional Properties
```typescript
interface Hero {
name: string;
house: string;
title?: string; // An optional property
}
const jonSnow: Hero = {
name: "Jon Snow",
house: "Stark",
title: "King in the North",
};
console.log(jonSnow.title);
```
### 2.7.4 Index Signatures
```typescript
interface Hero {
name: string;
house: string;
[key: string]: any; //allowing any property
}
const jonSnow: Hero = {
name: "Jon Snow",
house: "Stark",
title: "King in the North",
};
console.log(jonSnow.title);
```
## 2.8 How to fix error "Object literal may only specify known properties, and X does not exist in type Y"
```typescript
interface Alliance {
houses: string[];
pact: string;
}
const newAlliance : Alliance = {
houses: ["Stark", "Tully"],
pact: "Mutual Defense",
location: "Riverrun", // Error: Object literal may only specify known properties, and 'location' does not exist in type 'Alliance'.
};
```
### 2.8.1 Extending Interfaces
```typescript
interface Alliance {
houses: string[];
pact: string;
}
interface DetailedAlliance extends Alliance {
location: string;
}
// The new declaration is accepted
const newAlliance: DetailedAlliance = {
houses: ["Stark", "Tully"],
pact: "Mutual Defense",
location: "Riverrun",
};
```
### 2.8.2 Optional Properties
Sometimes, not all details are known or necessary. We can mark some properties as optional:
```typescript
interface Alliance {
houses: string[];
pact: string;
location?: string;
}
// The new declaration is accepted
const newAlliance: Alliance = {
houses: ["Stark", "Tully"],
pact: "Mutual Defense",
location: "Riverrun",
};
```
### 2.8.3 Index Signatures
```typescript
interface Alliance {
houses: string[];
pact: string;
[key: string]: any;
}
// The declaration, no matter how detailed, is accepted
const newAlliance : Alliance = {
houses: ["Stark", "Tully"],
pact: "Mutual Defense",
location: "Riverrun",
};
```
### 2.8.4 Type Assertion
Use this tool with caution, you're telling TypeScript to trust our intention.
```typescript
interface Alliance {
houses: string[];
pact: string;
}
const newAlliance = {
houses: ["Stark", "Tully"],
pact: "Mutual Defense",
location: "Riverrun",
} as Alliance;
```
### Other example
```typescript
//error
interface HouseWithKnights {
name: "Stark" | "Lannister";
knights: number;
}
interface HouseWithCastle {
name: "Tully" | "Greyjoy";
castle: string;
}
const declareAllegiance = (house: HouseWithKnights | HouseWithCastle) =>{}
//Error: Object literal may only specify known properties, and 'knights' does not exist in type 'HouseWithCastle'.
//Fix 1: Aligning Declaration with Expected Interface
declareAllegiance({ name: "Tully", castle: "Riverrun" });
//or
declareAllegiance({ name: "Stark", knights: 100 });
//Fix 2: Creating a More Inclusive Interface
interface NobleHouse {
name: string;
knights?: number;
castle?: string;
}
const declareAllegianceFlexibly = (house: NobleHouse) =>{
console.log(`House ${house.name} has declared their allegiance.`);
}
declareAllegianceFlexibly({ name: "Tully", knights: 100 }); // Now works without error
//Fix 3: Using Type Assertions
declareAllegiance({ name: "Tully", knights: 100 } as HouseWithKnights);
// Fix 4: Extending Interfaces for Complex Scenarios
interface BaseHouse {
name: string;
}
interface HouseWithKnights extends BaseHouse {
knights: number;
}
interface HouseWithCastle extends BaseHouse {
castle: string;
}
type HouseWithKnightsAndCastle = HouseWithKnights & HouseWithCastle;
function declareAllegianceComplex(house: HouseWithKnightsAndCastle) {
console.log(`House ${house.name} with ${house.knights} knights guards ${house.castle}.`);
}
declareAllegianceComplex({ name: "Tully", knights: 100, castle: "Riverrun" });
```
## 2.9 Errors related to function signatures
## 2.9.1 Expected X Arguments, But Got Y
```typescript
//error
const sendRaven = (message: string, location: string) => {
console.log(`Sending raven with message: ${message} to ${location}`);
}
sendRaven("White Walkers are coming"); // Error: Expected 2 arguments, but got 1.
//1. Fulfill the arguments
sendRaven("White Walkers are coming", "Winterfell");
//2. give default value for location
const sendRaven = (message: string, location: string = "King's Landing") =>{
console.log(`Sending raven with message: ${message} to ${location}`);
}
sendRaven("All hail the King in the North!");
```
## 2.9.2 Expected X Arguments, But Got Y
```typescript
type Advisor = (name: string, title: string) => string;
const tyrionAdvisor: Advisor = (name: string) => `${name} is the Hand of the Queen`; // Error: Type provides no match for the signature.
//1. fulfill the type of argurments
const tyrionAdvisor: Advisor = (name: string, title: string) => `${name} is ${title}`;
//2. make title optional
type Advisor = (name: string, title?: string) => string;
```
## 2.9.3 Cannot Invoke an Expression Whose Type Lacks a Call Signature
```typescript
const spellbook = {
decipher: "Ancient Texts Deciphering Spell",
};
spellbook.decipher(); // Error: Cannot invoke an expression whose type lacks a call signature.
//1. Define the spellbook with a callable function
const spellbook = {
decipher: (text: string) => `Deciphering: ${text}`,
};
spellbook.decipher("Valyrian scrolls");
//2. If the spellbook must remain unchanged, create a separate function to invoke the spell.
const decipherSpell = (text: string) => {
console.log(`Deciphering: ${text}`);
}
decipherSpell(spellbook.decipher);
```
## 2.9.4 A Function Whose Declared Type is Neither 'void' Nor 'any' Must Return a Value
```typescript
const sendWarning = (signal: string): string =>{
// No return statement here
}
sendWarning("White Walkers!"); // Error: Function lacks return statement.
//1. Ensure the function fulfills its duty by returning the appropriate warning signal.
const sendWarning = (signal: string): string =>{
return `Warning: ${signal}`;
}
//2. If no specific warning needs to be returned, change the function's return type to void.
const sendWarning = (signal: string): void => {
console.log(`Warning: ${signal}`);
}
```