Type checking of classes
Basic Concepts of Type Checking
TypeScript's type checking system is one of its core features. Type checking occurs during the compilation phase, ensuring type correctness through static code analysis. The TypeScript compiler checks whether variables, function parameters, return values, etc., conform to the expected types.
let num: number = 42;
num = "hello"; // Error: Type 'string' is not assignable to type 'number'
Primitive Type Checking
TypeScript supports all primitive types in JavaScript and provides additional type-checking capabilities.
// Boolean
let isDone: boolean = false;
// Numbers
let decimal: number = 6;
let hex: number = 0xf00d;
// Strings
let color: string = "blue";
// Arrays
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // Generic syntax
// Tuples
let x: [string, number];
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
Interfaces and Type Checking
Interfaces are one of the primary tools for complex type checking in TypeScript.
interface Person {
name: string;
age: number;
address?: string; // Optional property
readonly id: number; // Readonly property
}
function greet(person: Person) {
return `Hello, ${person.name}`;
}
const user = { name: "Alice", age: 30, id: 1 };
greet(user); // OK
const invalidUser = { name: "Bob" };
greet(invalidUser); // Error: Missing required properties 'age' and 'id'
Classes and Type Checking
TypeScript's type checking for classes includes properties, methods, and access modifiers.
class Animal {
private name: string;
protected age: number;
public readonly species: string;
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
// console.log(this.name); // Error: 'name' is private
console.log(this.age); // OK: 'age' is protected
}
}
const dog = new Dog("Buddy", 5, "Canine");
dog.move(10);
dog.bark();
// dog.age = 6; // Error: 'age' is protected
Generic Type Checking
Generics provide a powerful way to create reusable components while maintaining type safety.
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString"); // Explicitly specify type
let output2 = identity(42); // Type inference
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = (x, y) => x + y;
Advanced Type Checking
TypeScript offers various advanced types to meet complex scenarios.
// Union types
let padding: string | number;
padding = "1em"; // OK
padding = 10; // OK
padding = true; // Error
// Type aliases
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello"; // OK
value = 42; // OK
// Intersection types
interface Named {
name: string;
}
interface Aged {
age: number;
}
type Person = Named & Aged;
let person: Person = { name: "Alice", age: 30 }; // OK
let invalidPerson: Person = { name: "Bob" }; // Error: Missing 'age'
// Type assertions
let someValue: any = "this is a string";
let strLength1: number = (<string>someValue).length; // Angle-bracket syntax
let strLength2: number = (someValue as string).length; // 'as' syntax
Type Guards and Type Inference
TypeScript uses type guards to narrow down types, improving the precision of type checking.
// 'typeof' type guard
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
// 'instanceof' type guard
class Bird {
fly() {
console.log("flying");
}
}
class Fish {
swim() {
console.log("swimming");
}
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
// Custom type guard
interface Cat {
meow(): void;
}
function isCat(animal: any): animal is Cat {
return (animal as Cat).meow !== undefined;
}
function handleAnimal(animal: Bird | Fish | Cat) {
if (isCat(animal)) {
animal.meow();
} else if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
Mapped Types and Conditional Types
TypeScript's mapped types and conditional types provide powerful type transformation capabilities.
// Mapped types
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;
// Conditional types
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<42>; // "number"
type T2 = TypeName<true>; // "boolean"
Type Compatibility Checking
TypeScript's type system is based on structural typing, offering flexibility but requiring an understanding of its rules.
interface Named {
name: string;
}
class Person {
constructor(public name: string) {}
}
let p: Named;
p = new Person("Alice"); // OK, because structurally compatible
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error: 'y' requires two parameters, 'x' only one
enum Status {
Ready,
Waiting,
}
enum Color {
Red,
Blue,
Green,
}
let status = Status.Ready;
status = Color.Red; // Error: Different enum types are incompatible
Declaration Merging and Type Checking
TypeScript allows declaration merging, which affects type-checking behavior.
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 }; // Merged interface
class Album {
label: Album.AlbumLabel;
}
namespace Album {
export class AlbumLabel {}
}
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
export let suffix = "";
export let prefix = "Hello, ";
}
console.log(buildLabel("Sam Smith")); // "Hello, Sam Smith"
Module Type Checking
TypeScript provides full type-checking support for module systems.
// math.ts
export function square(x: number): number {
return x * x;
}
export const PI = 3.14;
// app.ts
import { square, PI } from "./math";
console.log(square(4)); // 16
console.log(PI); // 3.14
// Type imports
import type { SomeType } from "./some-module";
let value: SomeType;
// Dynamic imports
async function load() {
const math = await import("./math");
console.log(math.square(2));
}
Decorator Type Checking
Decorators are a special kind of declaration that can be attached to class declarations, methods, accessors, properties, or parameters.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
Utility Types
TypeScript provides a series of built-in utility types to assist with type transformations.
interface Todo {
title: string;
description: string;
completed: boolean;
}
// Partial: Make all properties optional
type PartialTodo = Partial<Todo>;
// Readonly: Make all properties readonly
type ReadonlyTodo = Readonly<Todo>;
// Pick: Select a subset of properties
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit: Exclude a subset of properties
type TodoInfo = Omit<Todo, "completed">;
// Record: Construct a type with property keys of K and property values of T
type Page = "home" | "about" | "contact";
type PageInfo = Record<Page, { title: string }>;
// Exclude: Exclude from T those types assignable to U
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract: Extract from T those types assignable to U
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable: Exclude null and undefined from T
type T2 = NonNullable<string | number | undefined>; // string | number
// ReturnType: Obtain the return type of a function
type T3 = ReturnType<() => string>; // string
// InstanceType: Obtain the instance type of a constructor function type
class C {
x = 0;
y = 0;
}
type T4 = InstanceType<typeof C>; // C
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn