基础类型
以冒号开头后跟类型名称.
let isDone: boolean = false;
let num: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
集合是特殊的数组类型, 它允许不同类型的元素存在于同一个数组中:
let x: [string, number]
x = ["hello", 10]
x[3] = "world"
// [ 'hello', 10, , 'world' ]
console.log(x)
枚举类型:
enum Color {Red, Green, Blue}
let c: Color = Color.Green
// 1
console.log(c)
通用类型any:
let notSure: any = 4
notSure = 'hello'
let list: any[] = [1, true, 'free']
list[1] = 100
void: 在声明函数则代表无返回值; 声明变量则代表只能赋值undefined, null
function warnUser(): void {
//
}
let unusable: void = undefined;
null和undefined: 一般很少使用, 直接用any即可. 如果非要声明变量为undefined, null, 也可直接使用void:
let u: undefined = undefined;
let n: null = null;
类型转换:
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length
let strLength1: number = (someValue as string).length
变量声明
var不推荐使用, 因为ES5中没有块级作用域:
// 10 10 10 10 10 10 10 10 10 10
for (var i = 0; i < 10; i++) {
setTimeout(() => console.log(i), 100 * i)
}
一般遇到这种情况, 只能修改成如下代码:
// 0 1 2 3 4 5 6 7 8 9
for (var i = 0; i < 10; i++) {
((i) => {
setTimeout(() => console.log(i), 100 * i)
})(i)
}
let具有块级作用域, 所以用let来替换var.
let语法可用于数组/对象的解析:
let [first, second] = [1, 2]
// 1 2
console.log(first, second)
let [first1, ...rest] = [1, 2, 3, 4]
// 1 [2, 3, 4]
console.log(first1, rest)
let o = {
a: 'foo',
b: 12,
c: 'bar'
}
let {a, b} = o
// foo 12
console.log(a, b)
let {c, ...passthrough} = o
// bar {a: 'foo', b: 12}
console.log(c, passthrough)
也可以指定别名:
let {a: newName1, b: newName2} = o
// equal to
let newName1 = o.a
let newName2 = o.b
也可以指定默认值:
let {a, b = 1001} = o
const专门用于定义常量.
ES6中新引入的"..."用于解构数组或变量:
let first: number[] = [1, 2]
let second: number[] = [3, 4]
let both = [0, ...first, ...second, 5]
// [0, 1, 2, 3, 4, 5]
console.log(both)
let defaults = {a: 1, b: 2}
let search = {...defaults, c: 3}
// { a: 1, b: 2, c: 3 }
console.log(search)
接口
编写一个函数, 指定参数类型, 之前可以这么写:
function func(obj: {label: string}) {
console.log(obj.label)
}
let obj = {size: 10, label: 'object'}
func(obj)
而使用接口, 代码可修改如下:
interface Obj {
label: string
}
function func(obj: Obj) {
console.log(obj.label)
}
let obj = {size: 10, label: 'object'}
func(obj)
可选属性"?"
interface SquareConfig {
color? :string;
width?: 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'})
console.log(mySquare)
这里"color?"表明color属性为可选, 而{color: string; area: number}表明函数返回值的类型.
只读属性readonly
interface Point {
readonly x: number,
readonly y: number,
}
let p1: Point = {x: 10, y: 20}
// error
p1.x = 5
使用ReadonlyArray<T>为只读数组:
let a: number[] = [1, 2, 3, 4]
let ro: ReadonlyArray<number> = a
// error
ro[0] = 12
// error
a = ro
可使用as将只读数组赋值给一般数组:
a = ro as number[]
属性检查
针对上例函数中, 我们调用:
let mySquare = createSquare({colour: 'red', width: 100})
这里TS会直接报错, 因为它认定需要传入的参数为color/width, 没有colour属性.
假定我们程序里要求允许有其它的属性, 则需要修改interface:
interface SquareConfig {
color?: string,
width?: number,
[propName: string]: any,
}
函数类型
我们可定义函数的参数和返回值:
interface SearchFunc {
(source: string, subString: string): boolean,
}
let mySearch: SearchFunc
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub)
return result > -1
}
let search: SearchFunc
search = function(src, sub) {
let result = src.search(sub)
return result > -1
}
这一般适用于大量参数相同的函数的设定.
索引类型
数组的索引, 默认为整数, 但实际上会被转换为字符串. 我们可设定索引类型, 并可设定为只读:
interface StringArray {
readonly [index: number]: string
}
let myArray: StringArray
myArray = ['Alice', 'Bob']
let myStr: string = myArray[0]
console.log(myStr)
// error
// myStr[2] = 'Mallory'
class类型
我们可以为class定义一个接口:
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) {}
}
我们甚至可以在接口中声明方法, 在类中定义方法:
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) {}
}
如果我们为构造函数实现check:
interface ClockInterface {
new (hour: number, minute: number);
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) {}
}
这将报错! 因为TS的class中, 检查的是具体实例. 而构造函数属于静态方法, 无法被检查.
如果要检查构造函数, 只能将构造函数当做一个方法来对待:
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 tick");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
接口扩展
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;
console.log(square);
混合类型
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;
Classes
我们定义一个简单的class: 属性greeting, 构造函数和方法greet
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
let greeter = new Greeter("world");
console.log(greeter.greet());
继承
使用extends关键字实现继承操作:
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);
public, private和protected
默认情况下, class的属性/构造函数/方法, 均为public.
private的属性/方法, 只能在本class内部调用. 在外部或者继承中, 无法调用private声明的属性和方法.
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
// error
new Animal('cat').name;
private变量/方法是无法进行比较的, 所以下例中animal = employee会报错:
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;
// error
animal = employee;
protected不仅允许class内部调用, 还允许继承clas调用:
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());
// error
console.log(howard.name);
Readonly
我们可以设定属性为只读, 那除了构造函数中进行初始化以外, 其它任何地方都不可以对其进行修改:
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');
// error
dad.name = 'Man with the 3-piece suit';
Static
静态属性与实例无关, 与类有关.
class Grid {
static origin = {x: 0, y: 0};
calc(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);
let grid2 = new Grid(5.0);
console.log(grid1.calc({x: 10, y: 10}));
console.log(grid2.calc({x: 10, y: 10}));
Abstract Classes
抽象类不同于接口在于它可实现其方法:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earth...');
}
}
函数
一般函数可声明为如下格式:
let myAdd = (x: number, y: number) : number => {
return x + y;
};
console.log(myAdd(1, 3));
函数中某些参数可选, 或者可指定默认值:
let myAdd = (x: number, y?: number) : number => {
return x + y;
};
let myAdd1 = (x: number, y = 3) : number => {
return x + y;
};
剩余参数可使用"..."语法糖:
let func = (x: number, ...rest: any[]) => {
console.log(rest)
};
func(1, 2, 3, 4);