控制类的属性是否必须在构造函数中初始化(true if strict; false otherwise)
class A {
name: string // Error: Property 'name' has no initializer and is not definitely assigned in the constructor.
constructor() {}
}
如果想要关闭这个检查,可以使用明确赋值断言运算符
来告诉 TypeScript 该属性已经被初始化了!
class A {
name!: string
constructor() {}
}
class A {
// 只读属性:只能在声明时或构造函数中初始化
readonly name: string = 'world'
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName // 可以在构造函数中修改
}
}
}
可重载
class Point {
x: number = 0
y: number = 0
constructor(x: number, y: number)
constructor(xy: string)
constructor(x: number | string, y: number = 0) {}
}
没有参数类型 Parameters<>
没有返回类型 ReturnType<>
可使用参数属性
class Point {
constructor(readonly x: number, public y: number) {}
}
方法里面获取类的其他属性或者其他方法,需要使用 this
let x: number = 0
class C {
x: string = 'hello'
m() {
x = 'world' // Type 'string' is not assignable to type 'number'. 指的是外层的 x
}
}
Getters / Setters
如果只有
,则属性 getter
readonly
如果
的参数类型没有指定,会自动从 setter
的返回值类型推断getter
参数类型不匹配,也可以(from 4.3)setter
class Thing {
_size = 0
get size(): number {
return this._size
}
set size(value: boolean | number | string) {
const num = Number(value)
if (!Number.isFinite(num)) {
this._size = 0
return
}
this._size = num
}
}
class A {
[s: string]: ((s: string) => boolean) | boolean
check(s: string) {
return this[s] as boolean
}
}
public / private / protected
可以在类的内部和外部访问,包括子类。可以被继承,可以被重写,可以省略
关键字public
可以在类的内部和子类内部访问,但是不能在外部访问。
可以被继承,可以被重写。重写时可以改变可见性,将属性或方法从
改为其他,重新暴露出来protected
class Greeter {
public greet() {
console.log(`Hello, ${this.getName()}`) // ok
}
protected getName() {
return 'hi'
}
}
class SpecialGreeter extends Greeter {
public howdy() {
console.log(`Howdy, ${this.getName()}`) // ok
}
}
const g = new SpecialGreeter()
g.greet() // ok
g.getName() // error
跨层级保护访问(Cross-hierarchy protected access)
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: Derived1) {
other.x = 10 // error 属性“x”受保护,只能在类“Derived1”及其子类中访问
}
}
比
更严格,只能在类的内部访问,不包括子类内部。不能被继承,不能被重写protected
class Base {
private x = 0
}
class Derived extends Base {
x = 1 // error 类“Derived”错误扩展基类“Base”。属性“x”在类型“Base”中是私有属性,但在类型“Derived”中不是。
private x = 0 // error 类“Derived”错误扩展基类“Base”。类型具有私有属性“x”的单独声明。
}
"不能被重写" 这一点与js不同, es6 中可以重写私有属性
class Base {
#x = 0
#getX() {
return this.x
}
}
class Derived extends Base {
#x = 1
#getX() {
return this.#x
}
}
typescriptlang.org 中关于假私有或者软私有的描述好像不准确,在 typescript playground 中测试,没有编译成
的情况WeakMaps
可以通过类构造函数对象本身访问
静态成员也有
、 public
、 protected
可见性private
静态成员不能有关键字
、 name
、 length
等call
原理同 static block ES2022
class Box<Type> {
contents: Type
constructor(value: Type) {
this.contents = value
}
}
const b = new Box('hello!') // b: Box<string>
静态成员不可以是泛型的
class Box<Type> {
static defaultValue: Type // error 静态成员不能引用类类型参数
}
静态成员本来就是属于类的,而不是属于实例的,所以不应该依赖于实例的类型参数
this
class MyClass {
name = 'MyClass'
getName = () => {
return this.name
}
}
const c = new MyClass()
const g = c.getName
console.log(g()) // MyClass,箭头函数的 this 指向定义时的 this,如果 getName 是普通函数,则 this 为 undefined
上面的箭头函数虽然解决了this的问题,但是也有弊端
会使用更多的内存,因为每个实例都会保存一份getName方法的拷贝
不能在子类中使用 super.getName()
class MyClass {
name = 'MyClass'
getName() {
return this.name
}
}
class MySubClass extends MyClass {
name = 'MySubClass'
getName = () => {
return `${super.getName()}Sub`
}
}
new MySubClass().getName() // MyClassSub
改成箭头函数
class MyClass {
name = 'MyClass'
// 此时可以把 getName 看成一个属性,而不是方法
getName = () => {
return this.name
}
}
class MySubClass extends MyClass {
name = 'MySubClass'
getName = () => {
return `${super.getName()}Sub` // error 父类定义的类字段“getName”无法通过 super 在子类中访问
}
}
new MySubClass().getName()
在方法或函数定义中,名为 this 的初始参数在 TypeScript 中具有特殊含义。这些参数在编译期间被删除:
function fn(this: SomeType, x: number) {
/* ... */
}
// JavaScript output
function fn(x) {
/* ... */
}
此时我们不使用箭头函数,而是使用
参数,可以解决意外错误调用的问题this
class MyClass {
name = 'MyClass'
getName(this: MyClass) {
return this.name
}
}
const c = new MyClass()
const g = c.getName
console.log(g()) // error 类型为“void”的 "this" 上下文不能分配给类型为“MyClass”的方法的 "this"
class Box {
contents: string = ''
// 1. 这里参数里面出现了一个this类型,它表示Box类的实例类型
// 如果被其他类继承,那么this类型会变成继承的类的实例类型
sameAs(other: this) {
return other.contents === this.contents
}
// 2. 这里出现了一个this类型,它表示Box类的实例类型
set(value: string) { // (method) Box.set(value: string): this
this.contents = value
return this
}
}
class DerivedBox extends Box {
otherContent: string = '?'
}
const base = new Box()
const derived = new DerivedBox()
derived.sameAs(base) // error 类型“Box”的参数不能赋给类型“DerivedBox”的参数
class Box {
static contents: string = ''
static set(value: string) { // 类类型 (method) Box.set(value: string): typeof Box
this.contents = value
return this
}
}
您可以在类和接口方法的返回位置使用
。当与类型缩小(如 if 语句)混合使用时,目标对象的类型将缩小为指定的 this is Type
。Type
interface Networked {
host: string
}
class FileSystemObject {
constructor(
public path: string,
private networked: boolean,
) {}
isDirectory(): this is Directory {
return this instanceof Directory
}
isFile(): this is FileRep {
return this instanceof FileRep
}
isNetworked(): this is Networked & this {
return this.networked
}
}
class Directory extends FileSystemObject {
children!: FileSystemObject[]
}
class FileRep extends FileSystemObject {
constructor(
path: string,
public content: string,
) {
super(path, false)
}
}
const fso: FileSystemObject = new FileRep('foo/bar.txt', 'foo')
let a: FileSystemObject[] | string | string | undefined
if (fso.isFile()) {
a = fso.content // const fso: FileRep
} else if (fso.isDirectory()) {
a = fso.children // const fso: Directory
} else if (fso.isNetworked()) {
a = fso.host // const fso: Networked & FileSystemObject
}
console.log(a)
与泛型一起使用
class Box<T> {
value?: T
hasValue(): this is { value: T } {
return this.value !== undefined
}
}
const box = new Box<string>()
const a = box.value // const a: string | undefined
if (box.hasValue()) {
const b = box.value // const b: string
}
抽象类是不能被实例化的,只能被继承
抽象属性和抽象方法只能存在于抽象类中,没有提供实现但必须在派生类中实现,且必须实现所有的
abstract class Base {
abstract a: number
abstract getName(): string
printName() {
console.log(`Hello, ${this.getName()}`)
}
}
class Derived extends Base {
a = 1
getName() {
return 'world'
}
}
接上例
function greet(Ctor: typeof Base) {
const instance = new Ctor() // error 无法创建抽象类的实例
instance.printName()
}
上科技
function greet(Ctor: new () => Base) {
const instance = new Ctor()
instance.printName()
}
greet(Base) // 类型“typeof Base”的参数不能赋给类型“new () => Base”的参数。无法将抽象构造函数类型分配给非抽象构造函数类型
greet(Derived) // ok