class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(this.name);
}
}
let zs: Person = new Person('张三', 18);
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
这个例子展示了最基本的继承:类从基类中继承了属性和方法。 这里, Dog
是一个 派生类,它派生自 Animal
基类,通过 extends
关键字。 派生类通常被称作 子类,基类通常被称作 超类。
因为 Dog
继承了 Animal
的功能,因此我们可以创建一个 Dog
的实例,它能够 bark()
和 move()
。
下面是一个更复杂的例子:
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
与前一个例子的不同点是,派生类包含了一个构造函数,它 必须调用 super()
,它会执行基类的构造函数。 而且,在构造函数里访问 this
的属性之前,我们 一定要调用 super()
。 这个是TypeScript强制执行的一条重要规则。
这个例子演示了如何在子类里可以重写父类的方法。 Snake
类和 Horse
类都创建了 move
方法,它们重写了从Animal
继承来的 move
方法,使得 move
方法根据不同的类而具有不同的功能。 注意,即使 tom
被声明为Animal
类型,但因为它的值是 Horse
,调用 tom.move(34)
时,它会调用 Horse
里重写的方法:
Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.
public
开放的public
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
private
私有的class Person {
public name: string;
public age: number = 18;
private type: string = 'human'
public constructor (name, age) {
this.name = name
this.age = age
}
}
protected
受保护的private
类似,但是可以被继承class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误
注意,我们不能在 Person
类外使用 name
,但是我们仍然可以通过 Employee
类的实例方法访问,因为Employee
是由 Person
派生而来的。
readonly
只读的在上面的例子中,我们不得不定义一个受保护的成员 name
和一个构造函数参数 theName
在 Person
类里,并且立刻给 name
和 theName
赋值。 这种情况经常会遇到。 参数属性可以方便地让我们在一个地方定义并初始化一个成员。
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
可以简写为:
class Person {
constructor(public name: string, public age: number) {
}
}
let passcode = "secret passcode";
class Employee {
// 私有成员,外部无法访问
private _fullName: string;
// 当访问 实例.fullName 的时候会调用 get 方法
get fullName(): string {
return this._fullName;
}
// 当对 实例.fullName = xxx 赋值的时候会调用 set 方法
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
static
关键字class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
function add(x: number, y: number): number {
return x + y
}
function add(x: number, y?: number): number {
return x + 10
}
function add(x: number, y: number = 20): number {
return x + y
}
function sum(...args: number[]): number {
let ret: number = 0
args.forEach((item: number): void => {
ret += item
})
return ret
}
sum(1, 2, 3)
let add = (x: number, y: number): number => x + y
TypeScript 编译器会根据一些简单的规则来推断你定义的变量的类型
当你没有标明变量的类型时,编译器会将变量的初始值作为该变量的类型
let num = 3
//此时我未标注 num 变量的类型 ,初始值为数字类型 , 变量num为数字类型
let str = 'string'
//此时我未标注 str 变量的类型 ,初始值为字符串类型 , 变量str为字符串类型
当然,类型推断不仅仅发生在简单数据类型上面,复杂数据类型上依然可以被TypeScript编译器进行推断
let arr = ['one','two','three']
//此时未标注 arr 数组中的每个元素类型 , 初始值为字符串,则相当于:
//let arr : string[]
//但如果数组中没有元素,为空数组,则类型为 never
let obj = {a:1,b:2}
//此时未标注对象内的数据类型,默认为初始值,为数字类型
//let obj : {a:number,b:number}
//不仅如此,ts编译器还会推断函数的返回值类型
const fun = (a:number,b:number) =>{
retrun a + b;
}
//const fun =(a:number,b:numer) => number
但在使用函数返回值类型推断时,在编写函数内部的代码就失去了函数返回值类型检测功能,使用函数返回值的类型需要明确的指定
正常情况下,TypeScirpt编译器时可以推断出变量类型的,开发者不需要编写类型注释,但在TypeScirpt编译器不能正常推断类型时,开发者需要编写类型注释
第一种情况:
如果一个变量被声明后,没用被立即初始化,那么编译器将不能正确推断出它的类型,将被赋予any类型
let anything
//此时变量未被及时初始化,编译器默认它为any类型:let anything :any
第二种情况:
当被调用的函数的返回值为any类型的时候,应该使用类型注释来声明它的类型
let json ='{"name":"张三"}';
let person = JSON.parse(json);
//let json:string
//let person:any => let person:{name:string}
第三种情况:
当变量有可能有多个类型时:
let num = [-10,-1,20]
//target => bollean|number
let target =false
for(let i=0;i0){
//不能将number类型分配给bollean类型
target = num[i];
}
}
第四种情况:
函数的参数必须标注类型,TypeScript并不能推断函数参数的类型
export default xxx
export const foo: string = 'bar';
export const bar: string = 'foo';
// 加载默认成员
import xxx from '模块标识'
// 按需加载模块成员
import {foo, bar} from '模块'