类成员
class Point {
x: number;
y: number;
}
const pt = new Point();
pt.x = 0;
pt.y = 0;
字段类型说明可选,如果没指定,则为any
类型。
class Point {
x = 0;
y = 0;
}
也可以自动推导出类型。
--strictPropertyInitialization
class GoodGreeter {
name: string;
constructor() {
this.name = "hello";
}
}
class OKGreeter {
// 使用断言,不在构造函数中初始化也可以
// Not initialized, but no error
name!: string;
}
readonly
class Greeter {
readonly name: string = "world";
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName;
}
}
}
readonly
防止在构造函数外,重新赋值
构造函数
class Point {
x: number;
y: number;
// Normal signature with defaults
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
class Point {
// Overloads
constructor(x: number, y: string);
constructor(s: string);
constructor(xs: any, y?: any) {
// TBD
}
}
Super调用
子类构造函数在使用this前,必须先调用super()
方法
class Point {
x = 10;
y = 10;
scale(n: number): void {
this.x *= n;
this.y *= n;
}
}
Getters / Setters
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
- 如果只有
get
,那属性为readonly
- 如果setter的参数类型没指定,则从getter返回类型推导
- getters和setters成员可见性必须一致
索引签名
class MyClass {
[s: string]: boolean | ((s: string) => boolean);
check(s: string) {
return this[s] as boolean;
}
}
类继承
implements
可以使用implements
来检查一个类是否满足特定的接口
interface Pingable {
ping(): void;
}
class Sonar implements Pingable {
ping() {
console.log("ping!");
}
}
class Ball implements Pingable {
// error
pong() {
console.log("pong!");
}
}
类可以实现多个接口class C implements A, B {
extends
类可以从一个基类继承,派生类有基类的所有属性和方法,也可以定义额外的成员。
class Animal {
move() {
console.log("Moving along!");
}
}
class Dog extends Animal {
woof(times: number) {
for (let i = 0; i < times; i++) {
console.log("woof!");
}
}
}
const d = new Dog();
// Base class method
d.move();
// Derived class method
d.woof(3);
覆盖方法
一个派生类可以覆盖基类的字段或属性。可以使用super.
语法来访问父类的方法。
class Base {
greet() {
console.log("Hello, world!");
}
}
class Derived extends Base {
greet(name?: string) {
if (name === undefined) {
super.greet();
} else {
console.log(`Hello, ${name.toUpperCase()}`);
}
}
}
const d = new Derived();
d.greet();
d.greet("reader");
初始化顺序
class Base {
name = "base";
constructor() {
console.log("My name is " + this.name);
}
}
class Derived extends Base {
name = "derived";
}
// Prints "base", not "derived"
const d = new Derived();
顺序
- 基类字段初始化
- 基类构造函数执行
- 派生类字段初始化
- 派生类构造函数执行
继承内置类型
class MsgError extends Error {
constructor(m: string) {
super(m);
// Set the prototype explicitly.
Object.setPrototypeOf(this, MsgError.prototype);
}
sayHello() {
return "hello " + this.message;
}
}
需要手动设置原型指向。
成员可见性
public
类成员的默认可见性为public
,一个public
成员可以在任何地方被访问。
protected
protected
成员只有当前类,子类中可见。
class Greeter {
public greet() {
console.log("Hello, " + this.getName());
}
protected getName() {
return "hi";
}
}
class SpecialGreeter extends Greeter {
public howdy() {
// OK to access protected member here
console.log("Howdy, " + this.getName());
}
}
const g = new SpecialGreeter();
g.greet(); // OK
// `Property 'getName' is protected and only accessible within class 'Greeter' and its subclasses.
g.getName();
暴漏protected
成员
class Base {
protected m = 10;
}
class Derived extends Base {
// No modifier, so default is 'public'
m = 15;
}
const d = new Derived();
console.log(d.m); // OK
跨层级protected
的访问
class Base {
protected x: number = 1;
}
class Derived1 extends Base {
protected x: number = 5;
}
class Derived2 extends Base {
f1(other: Derived2) {
other.x = 10;
}
f2(other: Base) {
// Property 'x' is protected and only accessible through an instance of class 'Derived2'. This is an instance of class 'Base'.
other.x = 10;
}
}
private
只能在当前类中访问
class Base {
private x = 0;
}
const b = new Base();
// Can't access from outside the class
console.log(b.x);
跨实例private
访问
class A {
private x = 10;
public sameAs(other: A) {
// No error
return other.x === this.x;
}
}
注意事项
protected
和private
只在类型检查中是强制的,编译成js,protected
和private
不会生效。
class Dog {
// js private
#barkAmount = 0;
personality = "happy";
constructor() {}
}
静态成员
类可以有static
成员,这些成员和类的实例无关,只能通过类的构造函数来访问。
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
静态成员也可以有public
, protected
和private
修饰。
class MyClass {
private static x = 0;
}
// Property 'x' is private and only accessible within class 'MyClass'.
console.log(MyClass.x);
静态成员也可以被继承
class Base {
static getGreeting() {
return "Hello world";
}
}
class Derived extends Base {
myGreeting = Derived.getGreeting();
}
特殊静态名称
覆盖Function
原型的属性并不安全。因为类是可以通过new
执行的函数,特定的static名称不能使用,比如Function
的name
, length
, call
属性。
class S {
// Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'S'
static name = "S!";
}
static
Blocks
class Foo {
static #count = 0;
get count() {
return Foo.#count;
}
static {
try {
const lastInstances = loadLastInstances();
Foo.#count += lastInstances.length;
}catch {}
}
}
泛型类
class Box<Type> {
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
const b = new Box("hello!");
this
class MyClass {
name = "MyClass";
getName() {
return this.name;
}
}
const c = new MyClass();
const obj = {
name: "obj",
getName: c.getName,
};
// Prints "obj", not "MyClass"
console.log(obj.getName());
this的值是函数如何被调用时确定的,getName
通过obj的引用调用的,所以this的值是obj而不是类的实例(c)
箭头函数
class MyClass {
name = "MyClass";
getName = () => {
return this.name;
};
}
const c = new MyClass();
const g = c.getName;
// Prints "MyClass" instead of crashing
console.log(g());
- this值能够保证在运行时正确,即使没有ts的检查
- 这将使用更过的内存,因为每个实例都有这个方法的拷贝
- 不能在派生类中使用
super.getName
,因为在原型链中没入口去查询基类的方法
this
参数
// TypeScript input with 'this' parameter
function fn(this: SomeType, x: number) {
/* ... */
}
class MyClass {
name = "MyClass";
getName(this: MyClass) {
return this.name;
}
}
const c = new MyClass();
// OK
c.getName();
// Error, would crash
const g = c.getName;
console.log(g());
ts会检查函数调用时this上下文是否正确。通过在方法定义时添加this
参数来代替使用箭头函数,静态分析方法是否被正确调用。
与箭头函数比较
- 类的方法可能会被不正确使用
- 类中函数只分配一次内存,而不是每个实例上都分配一次
- 基类方法可以通过
super
调用
this
类型
class Box {
contents: string = "";
// (method) Box.set(value: string): this
set(value: string) {
this.contents = value;
return this;
}
}
this
类型守卫
可以在类中的方法返回位置使用this is Type
,当和类型收缩结合时,目标对象的类型可以收缩为指定类型
class Box<T> {
value?: T;
hasValue(): this is { value: T } {
return this.value !== undefined;
}
}
const box = new Box();
box.value = "Gameboy";
// (property) Box<unknown>.value?: unknown
box.value;
if (box.hasValue()) {
// (property) value: unknown
box.value;
}
参数属性
class Params {
constructor(
public readonly x: number,
protected y: number,
private z: number
) {
// No body necessary
}
}
const a = new Params(1, 2, 3);
// (property) Params.x: number
console.log(a.x);
// Property 'z' is private and only accessible within class 'Params'.
console.log(a.z);
类表达式
类表达式和类的声明非常相似,唯一不同是类表达式不需要name
const someClass = class<Type> {
content: Type;
constructor(value: Type) {
this.content = value;
}
};
抽象类和成员
ts中类、方法和字段可以是抽象的(abstract)。
一个抽象的方法或者字段没有实现,而且这些成员必须存在于抽象类中,所以抽象类不能被直接实例化。
抽象类的角色就是提供一个基类,让子类实现所有抽象成员。
abstract class Base {
abstract getName(): string;
printName() {
console.log("Hello, " + this.getName());
}
}
// Cannot create an instance of an abstract class.
const b = new Base();
class Derived extends Base {
getName() {
return "world";
}
}
const d = new Derived();
d.printName();
抽象构造函数签名
function greet(ctor: new () => Base) {
const instance = new ctor();
instance.printName();
}
greet(Derived);
类的关系
class Point1 {
x = 0;
y = 0;
}
class Point2 {
x = 0;
y = 0;
}
// OK
const p: Point1 = new Point2();
class Empty {}
function fn(x: Empty) {
// can't do anything with 'x', so I won't
}
// All OK!
fn(window);
fn({});
fn(fn);