TypeScript基础

TypeScript是由微软开发的自由开源的编程语言是JavaScript的一个超集,TypeScript可编译出纯净简洁的JavaScript代码,且可运行在任何浏览器上Node.js环境中任何支持ECMAScript3或更高版本的JavaScript引擎中;TypeScript优势在于有更多的规则类型限制,代码具有更高的预测性可控性易于维护调试;对模块命名空间面向对象的支持,更容易组织代码开发大型复杂程序,且编译步骤可捕获运行之前的错误

基础类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let isDone: boolean = false;
let decLiteral: number = 6;
let name: string = "bob"; // 可以使用单引号或双引号

// 模版字符串:可定义多行文本和内嵌表达式,以${ expr }这种形式嵌入表达式
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`;
// 与下面定义sentence的方式效果相同
let sentence: string = "Hello, my name is " + name + ".\n\n" +
"I'll be " + (age + 1) + " years old next month.";

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; // 使用数组泛型Array<元素类型>

// 想传入一个string或null或undefined,可以使用联合类型string | null | undefined
对象解构

可以使用...name语法创建一个剩余变量列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
let input = [1, 2];
let [first, second] = input; // 解构数组赋值

function f([first, second]: [number, number]) {
}
f(input);

// 可以使用...name语法创建一个剩余变量列
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]

let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1

let [, second, , fourth] = [1, 2, 3, 4];

let o = {
a: "foo",
b: 12,
c: "bar"
}
let {a, b} = o;
let {a: newName1, b: newName2} = o; // 也可以给属性以不同的名字,这里的冒号不是指示类型的
let {a, b}: {a: string, b: number} = o; // 想指定它的类型, 仍然需要在其后写上完整的模式

// 用于函数声明
type C = {a: string, b?: number}
function f({a, b}: C): void {
// ...
}
function f({a, b} = {a: "", b: 0}): void { // 默认值
// ...
}
f(); // ok, default to {a: "", b: 0}

function f({a, b = 0} = {a: ""}): void {
// ...
}
f({a: "yes"}) // ok, default b = 0
f() // ok, default to {a: ""}, which then defaults b = 0
f({}) // error, 'a' is required if you supply an argument
默认值
1
2
3
function keepWholeObject(wholeObject: {a: string, b?: number}) {
let {a, b = 1001} = wholeObject;
}
元组
1
2
3
4
5
6
7
// 元组类型允许表示一个已知元素数量和类型的数组,访问和数组一样
let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型
console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString
x[6] = true; // Error, 布尔不是(string | number)类型
枚举
1
2
3
4
5
// 枚举类型,默认情况下,从0开始为元素编号
enum Color {Red, Green, Blue};
enum Color {Red = 1, Green = 2, Blue = 4};
let c: Color = Color.Green;
let colorName: string = Color[2]; // 由枚举的值得到它的名字
any类型
1
2
3
4
5
6
// 任意值
let notSure: any = 4;
notSure = "maybe a string instead";
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let list: any[] = [1, true, "free"];
void类型
1
2
3
4
5
6
// 空值,某种程度上来说,void类型像是与any类型相反,它表示没有任何类型
function warnUser(): void {
alert("This is my warning message");
}
// 声明一个void类型的变量没有什么大用,因为只能为它赋予undefined和null
let unusable: void = undefined;
null和undefined
1
2
3
4
5
// undefined和null两者各自有自己的类型分别叫做undefined和null
// 默认情况下null和undefined是所有类型的子类型,如可以把null和undefined赋值给number类型的变量
// 若指定了--strictNullChecks标记,null和undefined只能赋值给void和它们各自
let u: undefined = undefined;
let n: null = null;
never类型

never类型表示永不存在的值的类型,如那些总是会抛出异常根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型,变量也可能是never类型,当其被永不为真的类型保护所约束时,never类型是任何类型的子类型,也可以赋值给任何类型,但没有类型是never的子类型或可赋值给never类型即使any也不可以赋值给never

1
2
3
4
5
6
7
8
9
10
11
12
13
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
类型断言

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构,没有运行时的影响,只是在编译阶段起作用,可以使用尖括号<>as语法来实现断言;当在TypeScript里使用JSX时,只有as语法断言是被允许的;

1
2
3
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
// 要求labelledObj对象参数有一个名为label类型为string的属性
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
// 使用接口来描述:必须包含一个label属性且类型为string
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
可选属性

接口里的属性不全都是必需的,有些是只在某些条件下存在,或者根本不存在,在可选属性名字定义的后面加一个?符号,可以对可能存在的属性进行预定义,可以捕获引用了不存在的属性时的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
// 这里的{color: string; area: number}是返回值
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
只读属性

对象属性只能在对象刚刚创建的时候修改其值,对于数组,TypeScript提供ReadonlyArray<T>类型其与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改也不可以把整个ReadonlyArray赋值到一个普通数组,但可以用类型断言重写;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
// 断言重写
a = ro as number[];
额外的属性检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}
// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });

// 使用类型断言绕开检查
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

// 添加一个字符串索引签名
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
// 将这个对象赋值给一个另一个变量:因为squareOptions不会经过额外属性检查,所以编译器不会报错
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
函数类型

接口也可以描述函数类型,为了使用接口表示函数类型,需要给接口定义一个调用签名,它就像是一个只有参数列表返回值类型函数定义,参数列表里的每个参数都需要名字类型;定义后就可以像使用其它接口一样使用这个函数类型的接口,然后创建一个函数类型的变量,并将一个同类型的函数赋值给该变量。对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配,要求对应位置上的参数类型是兼容的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface SearchFunc {
(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
if (result == -1) {
return false;
} else {
return true;
}
}
mySearch = function(src, sub) {
let result = src.search(sub);
if (result == -1) {
return false;
} else {
return true;
}
}
可索引类型

可以描述那些能够通过索引得到的类型,如a[10]ageMap["daniel"]可索引类型具有一个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型

1
2
3
4
5
6
7
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

共有支持字符串数字两种索引签名,可同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型,当使用number来索引时,会将其转换成string然后再去索引对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// Error: indexing with a 'string' will sometimes get you a Dog!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
// 因为字符串索引声明了obj.property和obj["property"]两种形式都可以
// name的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示
interface NumberDictionary {
[index: string]: number;
length: number; // 可以,length是number类型
name: string // 错误,`name`的类型不是索引类型的子类型
}

interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error! 因为索引签名是只读的
实现接口

接口描述了类的公共部分而不是公共和私有两部分,其不会检查类是否具有某些私有成员

1
2
3
4
5
6
7
8
9
10
11
12
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}

class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
静态部分和实例部分

类是具有静态部分类型实例的类型两个类型,当用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误,当一个类实现了一个接口时,只对其实例部分进行类型检查constructor存在于类的静态部分,不在检查的范围内;

1
2
3
4
5
6
7
8
9
interface ClockConstructor {
new (hour: number, minute: number);
}

// error
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { }
}

createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)中会检查AnalogClock是否符合构造函数签名;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {console.log("beep beep");}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {console.log("tick tock");}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

扩展接口

接口也可以相互扩展,能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里,一个接口可继承多个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
混合类型

一个接口可以同时做为函数和对象使用,并带有额外的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
接口继承类

接口继承了一个时,它会继承类的成员但不包括其实现,就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样,接口同样会继承到类的private和protected成员,创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
select() { }
}

1
2
3
4
5
6
7
8
9
10
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");

构造函数的派生类必须调用super(),它会执行基类的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Animal {
name:string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) { // 0是默认参数
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) { //重写父类的方法,5是默认参数
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) { // 重写父类的方法,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);

成员都默认为public,也可以明确的将一个成员标记成public,当成员被标记成private时,它就不能在声明它的类的外部访问

1
2
3
4
5
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;

TypeScript使用的是结构性类型系统,若所有成员的类型都是兼容的,则认为它们的类型是兼容的;若一个类型里包含一个privateprotected成员,则只有当另外一个类型中也存在这样一个private成员, 且它们都是来自同一处声明时,才认为这两个类型兼容;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // Error: Animal and Employee are not compatible

protected成员在派生类中仍然可以访问构造函数也可以被标记成protected,意味着该类不能在包含它的类外被实例化,但能被继承;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee can extend Person
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");
let john = new Person("John"); // Error: The 'Person' constructor is protected

可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时构造函数里被初始化

1
2
3
4
5
6
7
8
9
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.
参数属性

参数属性可以方便地在一个地方定义并初始化一个成员,仅在构造函数里使用private name: string参数来创建和初始化name成员,把声明和赋值合并至一处

1
2
3
4
5
6
class Animal {
constructor(private name: string) { } // 定义并初始化成员name
move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
存取器

TypeScript支持通过getters/setters来截取对对象成员的访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
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);
}
静态属性

静态成员属性存在于类本身上而不是类的实例上,

1
2
3
4
5
6
7
8
9
10
11
12
13
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}));
抽象类

抽象类做为其它派生类的基类使用,一般不会直接被实例化,抽象类可以包含成员的实现细节,abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。抽象类中的抽象方法不包含具体实现并且必须在派生类中实现,抽象方法的语法与接口方法相似,两者都是定义方法签名但不包含方法体,抽象方法必须包含abstract关键字并且可以包含访问修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports...');
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type
构造函数

当调用new并执行了这个函数后,便会得到一个类的实例,这个构造函数也包含了类的所有静态属性,使用typeof Greeter意思是取Greeter类的类型,而不是实例的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
} else {
return Greeter.standardGreeting;
}
}
}
let greeter1: Greeter; // 被赋值为构造函数
greeter1 = new Greeter();
console.log(greeter1.greet());
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
函数

可给每个参数添加类型再为函数本身添加返回值类型,TypeScript能够根据返回语句自动推断出返回值类型,因此通常省略返回值类型;只要参数类型是匹配的就认为它是有效的函数类型,而不在乎参数名是否正确;在函数和返回值类型之前使用=>符号指定返回值类型

1
2
3
4
5
6
7
8
9
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x+y; };
// 写出完整函数类型
let myAdd: (x:number, y:number)=>number = function(x: number, y: number): number { return x+y; };
// 在赋值语句的一边指定了类型但是另一边没有类型的话,TypeScript编译器会自动识别出类型
let myAdd = function(x: number, y: number): number { return x + y; };
let myAdd: (baseValue:number, increment:number) => number = function(x, y) { return x + y; };

JavaScript中每个参数都是可选的,可传可不传,没传参时其值是undefined,在TypeScript中可在参数名旁使用?实现可选参数的功能,可选参数必须跟在必须参数后面;且可为参数提供一个默认值当用户没有传递该参数传递的值是undefined默认值生效,也叫有默认初始化值的参数;在所有必须参数后面的带默认初始化的参数都是可选的;带默认值的参数不需要放在必须参数的后面;若带默认值的参数出现在必须参数前面,用户必须明确的传入undefined值来获得默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"

若想同时操作多个参数,或不知道会有多少参数传递进来,在JavaScript里可通过arguments来访问所有传入的参数,在TypeScript中可把所有参数收集到一个变量剩余参数会被当做个数不限的可选参数

1
2
3
4
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

JavaScript中this的值函数被调用时才会指定,TypeScript中箭头函数能保存函数创建时的this而不是调用时的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}
let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function(this: Deck) {
// return function() { // this在函数调用时指定
return () => { // this在函数创建时就指定了
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker(); // 若不是实用箭头函数,这里调用this为Window对象

this参数在回调函数中,this: void表示addClickListener期望onclick是一个不需要this类型的函数

1
2
3
4
5
6
7
8
9
10
11
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
this.info = e.message;
};
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: {suit: string; card: number; }[]): number; // 重载
function pickCard(x: number): {suit: string; card: number; }; // 重载
function pickCard(x): any { // 注意这并不是重载列表的一部分
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
} else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);