阿里云主机折上折
  • 微信号
Current Site:Index > Common compilation errors and solutions

Common compilation errors and solutions

Author:Chuan Chen 阅读数:24946人阅读 分类: TypeScript

TypeScript, as a superset of JavaScript, provides type-checking capabilities at compile time, but developers often encounter various compilation errors. Here are some typical errors and their solutions.

Type Mismatch Error

Type mismatch is one of the most common errors in TypeScript. When the actual type of a variable or function does not match the declared type, the compiler throws an error.

let age: number = "25"; // Error: Type 'string' is not assignable to type 'number'

The solution is to ensure type consistency:

let age: number = 25; // Correct

For function parameter type mismatches:

function greet(name: string) {
  console.log(`Hello, ${name}`);
}
greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'

Correction:

greet("Alice"); // Correct

Undefined Variable Error

When using an undeclared variable, TypeScript will report an error.

console.log(undeclaredVar); // Error: Cannot find name 'undeclaredVar'

The solution is to declare the variable:

let undeclaredVar = "value";
console.log(undeclaredVar); // Correct

Confusing Optional and Default Parameters

Developers sometimes confuse the syntax for optional and default parameters.

function createUser(name: string, age?: number = 18) { // Error: Parameter cannot have question mark and initializer
  // ...
}

Correct usage:

// Optional parameter
function createUser(name: string, age?: number) {
  age = age || 18;
}

// Or use default parameter
function createUser(name: string, age: number = 18) {
  // ...
}

Module Import/Export Errors

Incorrect paths or names when importing/exporting modules can cause compilation failures.

import { helper } from './utils'; // Error: Module not found if file doesn't exist

Ensure the file path is correct:

import { helper } from './utils.ts'; // Correct (assuming the file exists)

For confusion between default and named exports:

// utils.ts
export default function helper() {}

// main.ts
import { helper } from './utils'; // Error: Module has no exported member 'helper'

Should be changed to:

import helper from './utils'; // Correct

Type Assertion Errors

Inappropriate type assertions can lead to runtime errors.

let value: any = "hello";
let length: number = (<string>value).length; // Correct but unsafe

value = 123;
length = (<string>value).length; // Compiles but causes runtime error

A safer approach is to use type guards:

if (typeof value === 'string') {
  length = value.length; // Safe
}

Generic Constraint Issues

When generic parameters do not meet constraints, an error is reported.

function getLength<T>(arg: T): number {
  return arg.length; // Error: Property 'length' does not exist on type 'T'
}

Add generic constraints:

interface Lengthwise {
  length: number;
}

function getLength<T extends Lengthwise>(arg: T): number {
  return arg.length; // Correct
}

Decorator Application Errors

Decorator syntax errors or incorrect application locations can cause compilation failures.

@sealed // Error: Decorators are not valid here
class Greeter {
  greeting: string;
}

Ensure decorators are applied in supported locations:

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed // Correct
class Greeter {
  greeting: string;
}

Enum Type Conflicts

Enum member naming conflicts or duplicate values can cause issues.

enum Color {
  Red = 1,
  Green = 1, // Allowed but not recommended
  Blue // 2
}

let color: Color = Color.Red; // May not behave as expected

Best practice is to assign unique values to each enum member:

enum Color {
  Red = 1,
  Green = 2,
  Blue = 3
}

Incomplete Interface Implementation

Omitting members when a class implements an interface will cause an error.

interface Person {
  name: string;
  age: number;
}

class Student implements Person { // Error: Property 'age' is missing
  name: string;
}

Fully implement the interface:

class Student implements Person {
  name: string;
  age: number;
}

Attempting to Modify Read-Only Properties

Attempting to modify read-only properties will cause a compilation error.

interface Point {
  readonly x: number;
  y: number;
}

let p: Point = { x: 10, y: 20 };
p.x = 5; // Error: Cannot assign to 'x' because it is a read-only property

The solution is to avoid modifying read-only properties:

p.y = 15; // Allowed to modify non-read-only properties

Function Overload Implementation Mismatch

Inconsistent function overload declarations with actual implementations will cause errors.

function padding(all: number): string;
function padding(topAndBottom: number, leftAndRight: number): string;
function padding(top: number, right: number, bottom: number, left: number): string;
function padding(a: number, b?: number, c?: number, d?: number) { // Implementation must be compatible with all overloads
  // ...
}

If the implementation is incompatible:

function padding(a: string, b?: number) { // Error: This overload signature is not compatible
  // ...
}

Type Alias Circular References

Circular references between type aliases will cause errors.

type A = B; // Error: Type alias 'A' circularly references itself
type B = A;

Break the circular reference:

type A = {
  b: B;
}
type B = {
  a?: A;
}

Index Signature Conflicts

When an interface has explicitly defined properties, index signatures can conflict.

interface Dictionary {
  [key: string]: string;
  name: string;
  age: number; // Error: Property 'age' of type 'number' is not assignable to string index type 'string'
}

The solution is to make the index signature more flexible:

interface Dictionary {
  [key: string]: string | number;
  name: string;
  age: number; // Now correct
}

Strict Null Check Issues

When strictNullChecks is enabled, potential null access will cause errors.

let element: HTMLElement | null = document.getElementById('app');
element.innerHTML = 'hello'; // Error: Object is possibly 'null'

Add null checks:

if (element !== null) {
  element.innerHTML = 'hello';
}

Or use non-null assertions (use with caution):

element!.innerHTML = 'hello';

Insufficient Type Extensions

Attempting to access non-existent properties on an object will cause errors.

interface BasicUser {
  name: string;
}

let user: BasicUser = { name: 'Alice' };
console.log(user.age); // Error: Property 'age' does not exist on type 'BasicUser'

The solution is to extend the interface:

interface FullUser extends BasicUser {
  age?: number;
}

let user: FullUser = { name: 'Alice' };
console.log(user.age); // Correct, age is an optional property

Async Function Type Errors

Forgetting to handle Promise return values can cause type issues.

async function fetchData(): Promise<string> {
  return "data";
}

let result = fetchData(); // result is Promise<string>, not string
console.log(result.length); // Error: Property 'length' does not exist on type 'Promise<string>'

Handle Promises correctly:

fetchData().then(data => {
  console.log(data.length); // Correct
});

Or use await in async functions:

async function process() {
  let data = await fetchData();
  console.log(data.length); // Correct
}

Unexpected Type Inference Results

Sometimes type inference results may not match expectations.

let x = null; // x is inferred as any
x = 123; // Allowed but unsafe

With strict mode enabled:

let y: string | null = null;
y = "text"; // Correct
y = 123; // Error: Type 'number' is not assignable to type 'string | null'

Union Type Operation Restrictions

Direct operations on union type variables will cause errors.

function padLeft(value: string, padding: string | number) {
  return Array(padding + 1).join(" ") + value; // Error: Operator '+' cannot be applied to types 'string | number' and 'number'
}

Use type checks:

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  return padding + value;
}

Class Static Member Access Errors

Confusing instance members with static members will cause errors.

class MyClass {
  static staticMethod() {}
  instanceMethod() {}
}

let instance = new MyClass();
instance.staticMethod(); // Error: Property 'staticMethod' does not exist on type 'MyClass'

Correct access methods:

MyClass.staticMethod(); // Correct
instance.instanceMethod(); // Correct

Failed Type Guards

Incorrect type guards can cause compilation errors.

function isString(test: any): boolean {
  return typeof test === "string";
}

function example(foo: any) {
  if (isString(foo)) {
    console.log(foo.length); // Error: Property 'length' does not exist on type 'any'
  }
}

Use proper type predicates:

function isString(test: any): test is string {
  return typeof test === "string";
}

function example(foo: any) {
  if (isString(foo)) {
    console.log(foo.length); // Correct
  }
}

Modifying Read-Only Properties via Mapped Types

Attempting to modify read-only properties via mapped types will cause errors.

type ReadonlyPerson = {
  readonly name: string;
  readonly age: number;
}

type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
}

let person: Mutable<ReadonlyPerson> = { name: "Alice", age: 30 };
person.name = "Bob"; // Correct, readonly removed

Improper Use of Conditional Types

Complex conditional types may lead to unexpected errors.

type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  "other";

let a: TypeName<string> = "string"; // Correct
let b: TypeName<boolean> = "other"; // Correct
let c: TypeName<string> = "number"; // Error: Type '"number"' is not assignable to type '"string"'

Declaration File Conflicts

Duplicate declarations can cause conflicts.

declare module "my-module" {
  export function foo(): string;
}

declare module "my-module" {
  export function bar(): number; // Merged declarations
}

// But the following causes conflicts
interface MyInterface {
  prop: string;
}

interface MyInterface {
  prop: number; // Error: Subsequent property declarations must have the same type
}

Misjudged Type Compatibility

The structural type system may lead to unexpected compatibility.

interface Named {
  name: string;
}

class Person {
  name: string = "";
}

let p: Named;
p = new Person(); // Correct, because structurally compatible

But the following is incompatible:

interface Named {
  name: string;
  age?: number;
}

class Person {
  name: string = "";
  age: number = 0;
}

let p: Named = new Person(); // Still correct
let p2: Person = { name: "Alice" }; // Error: Property 'age' is missing

Function Parameter Destructuring Type Errors

Incorrect type annotation placement when destructuring parameters.

function draw({ shape: Shape, xPos: number }) { // Error: 'number' is a type but used as value
  // ...
}

Correct usage:

function draw({ shape, xPos }: { shape: Shape, xPos: number }) {
  // ...
}

Type Query Errors

Using typeof on variables as types may cause errors.

let size = 10;
let arr: Array<typeof size>; // Error: 'size' refers to a value but is being used as a type

// Correct usage
type Size = typeof size; // Define type first
let arr: Array<Size>; // Correct

Class Expression Type Issues

Types in class expressions may not match expectations.

const MyClass = class<T> {
  content: T;
  constructor(value: T) {
    this.content = value;
  }
};

let instance = new MyClass("hello");
instance.content = 123; // Error: Type 'number' is not assignable to type 'string'

Explicitly specify type parameters:

let instance = new MyClass<string>("hello");
instance.content = "world"; // Correct

Namespace Merging Issues

Merging namespaces may cause conflicts.

namespace N {
  export let x = 1;
}

namespace N {
  export let x = 2; // Error: Duplicate identifier 'x'
}

Correct merging method:

namespace N {
  export let x = 1;
}

namespace N {
  export let y = x; // Can access previously exported x
}

Type Parameter Default Value Errors

Improper use of generic type parameter defaults.

function createArray<T = string>(length: number, value: T): Array<T> {
  return Array(length).fill(value);
}

let arr = createArray(3, 123); // T inferred as number
let arr2 = createArray(3); // Error: Expected 2 arguments but got 1

Index Access Type Errors

Errors may occur when accessing types via indices.

type Person = { name: string; age: number };
type Age = Person["age"]; // number
type NonExist = Person["address"]; // Error: Property 'address' does not exist

Template Literal Type Errors

Improper use of template literal types.

type Color = "red" | "blue";
type Quantity = "one" | "two";

type Sentence = `${Quantity} ${Color} apples`; // "one red apples" | "one blue apples" | "two red apples" | "two blue apples"

let s: Sentence = "three red apples"; // Error: Type '"three red apples"' is not assignable to type 'Sentence'

Type Predicate Scope

Type predicates have limited scope.

function isNumber(x: any): x is number {
  return typeof x === "number";
}

let x: string | number = Math.random() > 0.5 ? "hello" : 123;

if (isNumber(x)) {
  x.toFixed(2); // Correct
}
x.toFixed(2); // Error: 'toFixed' does not exist on type 'string'

Constructor Type Errors

Confusing constructor types with instance types.

class Animal {
  constructor(public name: string) {}
}

type AnimalConstructor = typeof Animal;
type AnimalInstance = InstanceType<AnimalConstructor>;

let create = (ctor: AnimalConstructor, name: string): AnimalInstance => {
  return new ctor(name);
};

let animal = create(Animal, "Fido"); // Correct

Type Extension Pollution

Global type extensions may cause unexpected behavior.

// Not recommended global extension
declare global {
  interface Array<T> {
    shuffle(): void;
  }
}

Array.prototype.shuffle = function() {
  // Implementation
};

// May conflict with extensions from other libraries

Type Import Confusion

Confusing type imports with value imports.

// utils.ts
export function helper() {}
export type HelperOptions = { /* ... */ };

// main.ts
import { helper, HelperOptions } from './utils';

let options: HelperOptions = {}; // Correct
helper(); // Correct

// Incorrect example
let HelperOptions = 123; // Error: Duplicate identifier 'HelperOptions'

Bypassing Type Compatibility Checks

Using any to bypass type checks may cause issues.

function unsafeCast<T>(value: any): T {
  return value;
}

let str: string = unsafeCast(123); // Compiles but poses runtime risks

A safer alternative:

function safeCast<T>(value: unknown): T | never {
  if (typeof value === "string") {
    return value as T;
  }
  throw new Error("Invalid cast");
}

Type Recursion Depth Limit

Excessively deep recursive types may cause errors.

type Json =
  | string
  | number
  | boolean
  | null
  | { [property: string]: Json }
  | Json[];

// Deep nesting may cause performance issues or errors
let deepJson: Json = {
  level1: {
    level2: {
      /* ... */
    }
  }
};

Type Parameter Constraint Not Satisfied

Generic parameters not meeting constraints.

function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}

longest(10, 20); // Error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'

Pass parameters that meet the constraints:

longest("Alice", "Bob"); // Correct
longest([1, 2], [3]); // Correct

Choosing Between Type Aliases and Interfaces

Inappropriately choosing type aliases or interfaces.

// Suitable for interfaces
interface Point {
  x: number;
  y: number;
}

// Suitable for type aliases
type PointTuple = [number, number];

Type Inference and Contextual Types

Contextual types may lead to unexpected inference results.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.