Classes
TypeScript offers full support for the class keyword introduced in ES2015.
Class Members
// 最基础的class
class Point {}
Fields
成员
class Point {
x: number;
y: number;
}
const point = new Point();
point.x = 0;
point.y = 0;
初始化成员,类型推导
class Point {
x = 0;
y = 0;
}
const point = new Point();
console.log(`x: ${point.x}; y: ${point.y}`);
--strictPropertyInitialization
选项开启后,没有初始化的成员必需在构造函数里初始化
// Error
class BadGreeter {
name: string; // Property 'name' has no initializer and is not definitely assigned in the constructor.
}
// Ok
class GoodGreeter {
name: string;
constructor() {
this.name = 'hello';
}
}
// Ok with `!`
class OkGreeter {
name!: string;
}
readonly
只读属性: 只有在声明和构造函数中去初始化
class Greeter {
readonly name: string = 'world'; // Ok
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName; // Ok
}
}
err() {
this.name = 'not ok'; // Error
}
}
const g = new Greeter();
g.name = 'also not ok'; // Error
Constructors
构造函数和普通函数类似,给参数添加类型声明、默认值、重载
class Point {
x: number;
y: number;
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
// Overloads
class Point2 {
constructor(x: number, y: string);
constructor(s: string);
constructor(xs: any, y?: any) {
// TBD
}
}
Super Calls
super()
: 如果有基类,通过this
访问成员前,必需调用 super()
class Base {
k = 4;
}
class Berived extends Base {
constructor() {
console.log(this.k); // Error: 'super' must be called before accessing 'this' in the constructor of a derived class.
super();
}
}
Methods
方法: Class 里面的函数属性,叫方法; 成员通过 this
来访问
class Point {
x = 10;
y = 10;
scale(n: number): void {
this.x *= n;
this.y *= n;
}
}
Getters / Setters
accessors 访问器
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
accessors 推理规则:
- 如果一个属性有
get
没有set
,那这个属性会自动变成readonly
- 如果一个属性的setter的参数类型没有指定,会去从对应属性的getter的返回值来推导
- 属性的Getters 和 setters必需有相同的访问权限
TypeScript 4.3之后, Getting和setting 可以有不同的类型
class Thin {
_size = 0;
get size(): number {
return this._size;
}
set size(value: string | number | boolean) {
let num = Number(value);
if (!Number.isFinite(num)) {
this._size = 0;
return;
}
this._size = num;
}
}
Index Signatures
索引签名: class里面可以定义索引签名;和对象类型里面定义索引签名一样(Type alias和interface)
class MyClass {
[s: string]: boolean | ((s: string) => boolean);
check(s: string) {
return this[s] as boolean;
}
}
Class Heritage
implements
Clauses
实现interface
interface Pingable {
ping(): void;
}
class Sonar implements Pingable {
ping(): void {
console.log('ping!');
}
}
class Ball implements Pingable {
// Error: 没有实现 ping函数
pong() {
console.log('pong!');
}
}
实现多个interface:class C implements A, B {
Cautions
对于实现interface中的可选属性,不会创建这个属性
interface A {
x: number;
y?: number;
}
class C implements A {
x = 0;
}
const c = new C();
c.y = 100; // Error: Property 'y' does not exist on type 'C'.
extends
Clauses
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();
d.move(); // Animal class method
d.woof(3); // Gog class method
Overriding Methods
方法重载:用supper.
语法访问父类方法
class Base {
greet() {
console.log('Hello world!');
}
}
class Derived extends Base {
greet(name?: string) {
if (name === undefined) {
super.greet(); // access base class methods
} else {
console.log(`Hello, ${name.toUpperCase()}`);
}
}
}
const d = new Derived();
d.greet();
d.greet('reader');
// Ok
const b: Base = d;
b.greet();
Type-only Field Declarations
// TODO
Initialization Order
初始化顺序
class Base {
name = 'base';
constructor() {
console.log('My name is ' + this.name);
}
}
class Derived extends Base {
name = 'derived';
}
const d = new Derived();
执行顺序:
- 基类成员初始化
- 基类构造函数被调用
- 子类成员初始化
- 子类构造函数被调用
Inheriting Built-in Types
class MyError extends Error {
constructor(m: string) {
super(m);
}
sayHello() {
return 'Hello ' + this.message;
}
}
Member Visibility
在class外面访问属性和方法的权限
public
默认就是public
class Greeter {
public greet() {
console.log('hi!');
}
}
const g = new Greeter();
g.greet();
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
g.getName(); // Error: Property 'getName' is protected and only accessible within class 'Greeter' and its subclasses.
Exposure of protected membersCross-hierarchy protected access
private
和protected
类似,但在子类中也无法访问
// 外部无法访问
class Base {
private x = 0;
}
const b = new Base();
console.log(b.x); // Error: Property 'x' is private and only accessible within class 'Base'.
// 子类无法访问
class Derived extends Base {
showX() {
console.log(this.x);
// Error: Property 'x' is private and only accessible within class 'Base'.
}
}
Cross-instance private access
Caveats
class MySafe {
private secretKey = 12345;
}
const s = new MySafe();
console.log(s.secretKey); // Error: Property 'secretKey' is private and only accessible within class 'MySafe'.
console.log(s['secretKey']); // OK
Static Member
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
static
Blocks in Classes
可以写一些初始化的代码;执行一次
class Foo {
static #count = 0;
get count() {
return Foo.#count;
}
// 静态代码块
static {
try {
const lastInstances = loadLastInstances();
Foo.#count += lastInstances.lenght;
} catch {}
}
}
Generic Classes
泛型 Class
class Box<Type> {
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
const b = new Box('hello!'); // const b: Box<string>
Type Parameters in Static Members
class Box<Type> {
static defaultValue: Type; // Error: Static members cannot reference class type parameters
}
this
at Runtime in Classes
前提:TS 不会改变 JS 的运行时行为 JS 的 this
包袱; by default, the value of this inside a function depends on how the function was called.
class MyClass {
name = 'MyClass';
getName() {
return this.name;
}
}
const c = new MyClass();
const obj = {
name: 'obj',
getName: c.getName, // 上面的 this 函数会被这里调用,所以 this 指的是 obj,不是 MyClass
};
// 打印的是 "obj", 不是 "MyClass"
console.log(obj.getName());
Arrow Functions
如果如上 this
丢失了上下文,用箭头函数替代方法定义
class MyClass {
name = 'MyClass';
getName = () => {
return this.name;
};
}
const c = new MyClass();
const g = c.getName;
// 打印 MyClass
console.log(g());
this
parameters
在方法或函数里面定义了 this
参数,TS 在编译时,会把这个参数擦除掉
// 编译前 TS 代码,带 this 参数的函数
function fn(this: SomeType, x: number) {
/* ... */
}
// 编译成 JS 后
function fn(x) {
/* ... */
}
this
Types
// TODO
this
-based type guards
Parameter Properties
把构造函数是的参数前加 权限关键字(public、private、protected、readonly
),则会把参数转变成类成员变量 (语法糖:少写代码)
class Params {
// x/y/z会转变成成员
constructor(
public readonly x: number,
protected y: number,
private z: number
) {
// No body necessary
}
}
const a = new Params(1, 2, 3);
console.log(a.x);
console.log(a.z); // Error: Property 'z' is private
Class Expressions
类表达式和类声明类似;区别在于类表达式不需要名字;会推导出一个名字
const someClass = class<Type> {
content: Type;
constructor(value: Type) {
this.content = value;
}
};
const m = new someClass('Hello world'); // const m: someClass<string>
Constructor Signatures
abstract
Classes and Members
Abstract Construct Signatures
Relationships Between Classes