TypeScript语法基础学习

121 阅读3分钟

介绍

本文是对TypeScript的简要介绍,主要选取可能会在项目中常用的内容

基础类型

TypeScript最明显的特点是提供了类型注解功能,这是一种轻量级的为函数或变量添加类型约束的方式。TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型,但注意各类型的表示,不要与String,Boolean等包装类型混淆,各类型的简单说明及示例如下:

布尔类型

类型为boolean,就是基本的true/false

let isDone: boolean = true

数字类型

类型为number,支持二进制、八进制、十进制和十六进制数字

let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制

字符串

类型为string,支持"'表示的字符串,也支持ES6的模板字符串

let name: string = 'Hello World'
let tips: string = `Welcome, ${name}` // 模板字符串

数组类型

[]泛型两种表示方法

let list: number[] = [1, 2, 3] // 数组表示
let list: Array<number> = [1, 2, 3] // 泛型表示

元组Tuple

元组类型表示一个已知元素数量和类型的数组,各元素的类型不必相同

let x: [string, number] = ['hello', 3]

枚举

类型为enum,是对JavaScript标准数据类型的一个补充

enum Color { Red = 1, Blue, Green } // 可以手动修改指定成员的值
let color: Color = Color.Red
let colorName: string = Color[2] // 值为'Blue',可以由值映射得到枚举名称

任意值

类型为any,表示不清楚该值的具体类型,类型检查器不对这些值进行检查而是直接让它们通过编译阶段的检查

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

空值

类型为void,表示没有任何返回值,常用于没有返回值的函数

function warnUser(): void {
    alert("This is my warning message");
}

Null 和 Undefined

undefinednull两者各自有自己的类型分别叫做undefinednull,默认情况下nullundefined是所有类型的子类型

Never

never类型表示的是那些永不存在的值的类型。 例如,never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是never类型,当它们被永不为真的类型保护所约束时

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

类型断言

类型断言好比java语言里的类型转换,但是不进行特殊的数据检查和解构,它没有运行时的影响,只是在编译阶段起作用,有两种方式:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length; // 尖括号方式
let strLength: number = (someValue as string).length; // as方式

变量声明

let声明

let对作用域要求更高,是ES6新增替代var的,当用let声明一个变量,它使用的是词法作用域块作用域,块作用域变量在包含它们的块或for循环之外是不能访问的,并且在块作用域内同层级不能重复定义

const声明

const拥有与let相同的作用域规则,但是不能对它们重新赋值,所有变量除了计划去修改的都建议使用const

解构数组

更方便的将数组内容赋值给变量的方式

let input = [1, 2, 3];
let [first, second] = input;
let [first, ...rest] = [1, 2, 3, 4]; // 可以使用...语法创建剩余变量,rest为[2, 3, 4]

也可以用于函数参数

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

对象解构

更方便的将对象属性赋值给变量的方式

let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let { a, b } = o; // a,b变量分别获区了o对象的a,b属性的值
let { a, ...rest } = o; // 可以使用...语法创建剩余变量,rest为{b: 12, c: 'bar'}
let { a: newName1, b: newName2 } = o; // 可以为a,b变量重命名分别为newName1,newName2
let { a, b = 1001 } = o; // 可以为b设置默认值,o.b为undefined时b变量为1001

也可以用于函数声明

function f({ a, b } = { a: "", b: 0 }): void {
    // ...
}
f(); // ok, default to { a: "", b: 0 }

展开

展开操作符正与解构相反,它允许将一个数组展开为另一个数组,或将一个对象展开为另一个对象

let list = [3, 4];
let newList = [0, ...list, 5]; // 值为[0, 3, 4, 5]

let defaults = { food: "spicy", price: "$$" };
let search = { ...defaults, food: "rich" }; // 值为{food: "rich", price: "$$"},注意food覆盖了前面展开的food属性值

接口(interface)

TypeScript的核心原则之一是对值所具有的结构进行类型检查,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约,与java中的接口与Bean的使用较为类似

基础用法

声明关键词为interface,接口中定义的参数为必需属性

interface LabelledValue {
  label: string;
}
function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

可选属性

在属性名和:之间加?可使该属性变为可选属性

interface Animal {
    name: string
    type?: string
}
let dog: Animal = {name: 'pig'}

只读属性

关键词为readonly,只读属性只能在对象刚刚创建的时候修改其值

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

额外属性的检查

对象字面量会被特殊对待而且会经过额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时会得到一个错误

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}
let mySquare = createSquare({ name: "red", width: 100 }); // Error,name是额外属性

// 方法一:类型断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
// 方法二:字符串索引签名
interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}
// 方法三:赋值给单独变量
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

函数类型

接口也可以表示函数类型,需要给接口定义一个调用签名,参数列表里的每个参数都需要名字和类型,实际实现时只要求参数类型保持一致

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;
}

类实现接口

接口可以被类实现,使用关键词implements

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

继承接口

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

interface Shape {
    color: string;
}
interface Square extends Shape {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;

混合类型

接口可以同时描述函数和对象类型

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;

类是ES6提供的面向对象开发的新语法,关键词为class

继承

类继承与java的类继承类似,关键词为extends,在子类构造函数中必须要调用super(),且要在使用this的属性之前

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: 成员没有写修饰符时默认为public,可被外部自由访问
  • protected: 只能在派生类中访问
  • private: 不能在类外部访问

readonly修饰符

关键词为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");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

参数属性

参数属性可以方便地让我们在一个地方定义并初始化一个成员

class Animal {
    constructor(private name: string) { } // 创建和初始化`name`成员放在一处
    move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

存取器

存取器通过get/set关键词来截取对对象成员的访问,只带有get不带有set的存取器自动被推断为readonly

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);
}

静态属性

关键词为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) { }
}

抽象类

关键词为abstract,抽象类可以包含成员的实现细节,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}

函数

函数类型

TypeScript为函数的参数和返回值增加了类型定义,有以下表示方法:

// myAdd has the full function type
let myAdd = function(x: number, y: number): number { return x + y; };

// The parameters `x` and `y` have the type number
let myAdd: (baseValue: number, increment: number) => number =
    function(x, y) { return x + y; };
// 箭头函数表示
let myAdd = (x: number, y: number): number => { return x + y; }

可选参数和默认参数

使用?表示可选参数,在参数列表中使用=设置默认参数

function buildName1(firstName: string, lastName?: string) {
    // ...
}
function buildName2(firstName: string, lastName = "Smith") {
    // ...
}

剩余参数

关键词为...,与展开操作的关键词一样,不过此处的含义是把函数参数列表中的多个参数放到一个数组变量中

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

泛型

泛型类型

TypeScript提供了与java类似的泛型语法用于创建可重用的组件,使用的也是尖括号<>

// 泛型方法
function identity<T>(arg: T): T {
    return arg;
}
// 泛型接口
interface GenericIdentityFn {
    <T>(arg: T): T;
}
// 泛型类
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

泛型约束

可以使用关键词extends来对泛型进行类型约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // T是Lengthwise的子类,所以arg有length属性
    return arg;
}