阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 实例类型与类类型

实例类型与类类型

作者:陈川 阅读数:4544人阅读 分类: TypeScript

在TypeScript中,类型系统是其核心特性之一,而实例类型与类类型是理解类型系统的重要概念。这两者之间的关系和区别对于编写类型安全的代码至关重要。

实例类型与类类型的定义

实例类型是指通过类实例化后对象的具体类型,而类类型则是指类本身的类型。在TypeScript中,类既是值也是类型,但它们的含义不同。

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// 类类型
const PersonClass: typeof Person = Person;

// 实例类型
const personInstance: Person = new Person("Alice", 30);

类类型的使用场景

类类型通常用于需要操作类本身的场合,比如工厂函数或类装饰器。typeof操作符可以获取类的类型。

function createInstance<T>(ctor: new (...args: any[]) => T, ...args: any[]): T {
  return new ctor(...args);
}

const person = createInstance(Person, "Bob", 25);

实例类型的特性

实例类型包含了类的实例属性和方法,但不包括静态成员。TypeScript会自动为类声明生成一个同名的实例类型接口。

class Point {
  static origin = { x: 0, y: 0 };
  x: number;
  y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }

  distance() {
    return Math.sqrt(this.x ** 2 + this.y ** 2);
  }
}

// 实例类型只包含实例成员
const p: Point = new Point(3, 4);
p.distance(); // 5
// p.origin; // 错误:实例类型不包含静态成员

类类型与构造签名

类类型通常表现为构造签名,描述如何通过new操作符创建实例。这在泛型约束中特别有用。

interface AnimalConstructor {
  new (name: string): Animal;
}

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

function createAnimal<T extends Animal>(ctor: new (name: string) => T, name: string): T {
  return new ctor(name);
}

const cat = createAnimal(Animal, "Mittens");

类表达式与类型

类表达式也可以产生类型,这些类型的使用方式与类声明相同。

const Rectangle = class {
  constructor(public width: number, public height: number) {}
  area() {
    return this.width * this.height;
  }
};

type RectangleType = InstanceType<typeof Rectangle>;
const rect: RectangleType = new Rectangle(10, 20);

泛型类中的类型关系

泛型类会为每个具体类型参数生成不同的实例类型,但共享相同的类类型。

class Box<T> {
  value: T;
  constructor(value: T) {
    this.value = value;
  }
}

const numberBox: Box<number> = new Box(42);
const stringBox: Box<string> = new Box("hello");

// 类类型相同
const BoxClass: typeof Box = Box;

继承关系中的类型

子类的实例类型会包含父类的所有成员,而类类型的继承关系则更为复杂。

class Animal {
  move() {
    console.log("Moving");
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof");
  }
}

// 实例类型兼容性
const animal: Animal = new Dog(); // 合法
// animal.bark(); // 错误:Animal类型没有bark方法

// 类类型兼容性
const AnimalClass: typeof Animal = Animal;
// const DogClass: typeof Animal = Dog; // 合法

私有字段与类型

私有字段会影响类型的兼容性,即使两个类结构相同,如果私有字段来自不同的声明,它们的实例类型就不兼容。

class A {
  private x = 1;
  show() {
    console.log(this.x);
  }
}

class B {
  private x = 1;
  show() {
    console.log(this.x);
  }
}

const a: A = new A();
// const b: A = new B(); // 错误:A和B不兼容

抽象类的类型

抽象类既可以用作类类型,也可以产生实例类型,但抽象类本身不能被实例化。

abstract class Shape {
  abstract area(): number;
  display() {
    console.log(`Area: ${this.area()}`);
  }
}

// 类类型
const ShapeClass: typeof Shape = Shape;

// 实例类型
class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }
  area() {
    return Math.PI * this.radius ** 2;
  }
}

const circle: Shape = new Circle(5);

接口与类类型

接口可以用来描述类类型的结构,包括静态成员和实例成员。

interface ClockInterface {
  currentTime: Date;
  setTime(d: Date): void;
}

interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}

class Clock implements ClockInterface {
  currentTime: Date;
  constructor(h: number, m: number) {
    this.currentTime = new Date(2023, 0, 1, h, m);
  }
  setTime(d: Date) {
    this.currentTime = d;
  }
}

function createClock(ctor: ClockConstructor, h: number, m: number): ClockInterface {
  return new ctor(h, m);
}

const clock = createClock(Clock, 12, 30);

类型查询与类

typeof操作符在类上下文中有特殊行为,可以获取类的类型,包括静态成员。

class Logger {
  static level = "INFO";
  log(message: string) {
    console.log(`[${Logger.level}] ${message}`);
  }
}

type LoggerType = typeof Logger;
// 等价于
interface LoggerType {
  new (): Logger;
  level: string;
}

const loggerClass: LoggerType = Logger;
console.log(loggerClass.level); // "INFO"

构造函数参数类型

可以使用ConstructorParameters工具类型来获取类构造函数的参数类型。

class User {
  constructor(public id: number, public name: string) {}
}

type UserParams = ConstructorParameters<typeof User>;
// 等价于 [number, string]

function createUser(...args: UserParams): User {
  return new User(...args);
}

const user = createUser(1, "Alice");

实例类型工具

TypeScript提供了InstanceType工具类型来获取类的实例类型。

class Map<K, V> {
  private data = new Map<K, V>();
  set(key: K, value: V) {
    this.data.set(key, value);
  }
  get(key: K): V | undefined {
    return this.data.get(key);
  }
}

type MapInstance<K, V> = InstanceType<typeof Map<K, V>>;
const stringMap: MapInstance<string, number> = new Map<string, number>();
stringMap.set("age", 30);

类与类型断言

在某些情况下,可能需要使用类型断言来处理类类型和实例类型之间的关系。

class Base {
  baseMethod() {}
}

class Derived extends Base {
  derivedMethod() {}
}

const base: Base = new Derived();
// (base as Derived).derivedMethod(); // 需要类型断言

// 类类型断言
const DerivedClass = Derived as typeof Base;
const derived = new DerivedClass();

装饰器中的类型

类装饰器接收的是类类型,可以修改或扩展类的行为。

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

@sealed
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return "Hello, " + this.greeting;
  }
}

类类型与映射类型

映射类型可以应用于类类型,生成新的类型。

class Product {
  constructor(
    public id: number,
    public name: string,
    public price: number
  ) {}
}

type PartialProduct = Partial<InstanceType<typeof Product>>;
const partialProduct: PartialProduct = { name: "Laptop" };

条件类型与类

条件类型可以根据类类型或实例类型进行类型判断。

type IsNumber<T> = T extends number ? true : false;

class Numeric {
  value: number;
  constructor(v: number) {
    this.value = v;
  }
}

type Test1 = IsNumber<InstanceType<typeof Numeric>>; // false
type Test2 = IsNumber<Numeric["value"]>; // true

类类型与索引访问

可以通过索引访问类型来获取类中的特定成员类型。

class Database {
  connection: { host: string; port: number };
  constructor(host: string, port: number) {
    this.connection = { host, port };
  }
}

type ConnectionType = Database["connection"];
// 等价于 { host: string; port: number }

类类型与函数重载

类方法可以像普通函数一样重载,这会影响实例类型。

class Overloaded {
  greet(name: string): string;
  greet(age: number): string;
  greet(value: string | number): string {
    if (typeof value === "string") {
      return `Hello, ${value}`;
    } else {
      return `You are ${value} years old`;
    }
  }
}

const overloaded = new Overloaded();
overloaded.greet("Alice"); // OK
overloaded.greet(30); // OK
// overloaded.greet(true); // 错误

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌