Common compilation errors and solutions
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
上一篇:TypeScript的生态系统