阿里云主机折上折
  • 微信号
Current Site:Index > Type checking of classes

Type checking of classes

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

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

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 ☕.