01、Hello TypeScript
江湖规矩,先来个 Hello 感受一下:
const hello = (name: string) => {
console.log(`Hello ${name}`)
}
hello('TypeScript');
很简单,通过在 name 后面使用冒号,说明参数是 string 类型,这就是 TypeScript 的基本用法。
02、原始数据类型
const a: string = 'string';
const b: number = 100;
const c: boolean = true;
const d: void = undefined;
const e: null = null;
const f: undefined = undefined;
const g: symbol = Symbol();
03、TypeScript 中的 object
所有非原始类型,包括对象、数组、函数,因此以下写法都是 OK 的:
const obj: object = {};
const ary: object = [];
const fn: object = function() {};
04、数组类型
数组的定义方式有两种:
// 1、Array<T>:先用Array说明是个数组,然后在<>里面规定数组里面的每一项的数据类型
const ary1: Array<number> = [1, 2, 3];
// 2、T[]:先规定数据类型,然后通过[]说明它是个数组
const ary2: number[] = [1, 2, 3];
实例:
function sum(...args: number[]) {
return args.reduce((total, current) => total + current, 0);
}
// sum(1, 2, 'c'); // 'c'会报错,因为不是number类型
05、元组
指的是明确变量类型及个数的数组。
const tuple: [number, string, boolean] = [1, 'string', true];
const [n, s, b] = tuple;
06、枚举 enum
通过 enum 关键字,给一组数据起一个固定的名字,每一个成员都有固定的值。
原始 Javascript 形式:
const STATUS_TYPES = {
NotStart: 0,
Starting: 1,
Started: 2
}
枚举形式:
enum STATUS_TYPES {
NotStart = 0,
Starting = 1,
Started = 2
}
索引枚举可省略数值不写,会默认从 0 开始累加,如以上可简写成:
enum STATUS_TYPES {
NotStart,
Starting,
Started
}
如需从 1 开始累加,则只需要把 NotStart = 1。字符串枚举则需每个依次定义,但不常用,这里也不做示例。
直接通过 enum 定义的枚举会影响编译结果,生成双向键值对的代码,即可通过索引访问名称 STATUS_TYPES[0] = 'NotStart',若不需要,可使用常量枚举,在 enum 前面加上 const 即可:
const enum STATUS_TYPES {
NotStart,
Starting,
Started
}
07、函数类型
在括号中指定入参,括号后面指定返参( return ):
function fn1(a: number, b: string, ...rest: any): string {
// 由于返参类型指定为 string,则只能返回字符串
return 'fn1';
}
fn1(100, 'string', {});
若不需要返参,则指定为 void :
function fn2(a: number, b: string, ...rest: any): void {
...
}
08、类型断言 as
TypeScript 会根据函数返回值等,自动推测出数据类型。
const nums = [10, 100, 200];
let res = nums.find( i => i > 1);
上面的代码中,res 会被自动推测为 number 或 undefined,而 undefined 不能用来计算,因此直接使用let square = res * res;会报错,我们可以通过 as 关键字来断言,指定类型:
// 通过as关键字来断言,告诉ts: _res是number类型
let _res = res as number;
// 此写法jsx语法下会冲突,不建议使用
// let _res = <number>res;
let square = _res * _res;
09、接口 interface
可以理解为一种约定,指定了各种属性的成员。
必填成员:直接指定字段名和类型 title: string
可选成员:使用 ? 关键字 subTitle?: string
只读成员:使用 readonly 关键字 readonly summary: string
任意成员:使用 prop 关键字 [prop: string]: string
实例:
interface Post {
title: string;
content: string;
subTitle?: string; // 可选成员
readonly summary: string // 只读成员
// [prop: string]: string // 任意成员,因此会和?冲突
}
function printPost(post: Post) {
console.log(post.title);
console.log(post.content);
}
printPost({
title: 'title',
content: 'content',
summary: 'summary'
})
10、类 class
用来描述一类具体对象的抽象成员,Typescript 增强了 class 的相关语法,关键字有以下几个:
public公有变量,外部可访问(默认,可不写)
protected 受保护的变量,只允许子类访问
private 私有变量,外部不可访问,子类也不能访问
readonly 只读变量,不允许修改
实例:
class Person {
public name: string = 'init name' // 公有变量,外部可访问(默认,可不写)
private age: number = 18 // 私有变量,外部不可访问
protected readonly gender: boolean = false // 受保护的变量,只允许子类访问(readonly需在后面)
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(msg: string): void {
console.log(`${this.name} say ${msg}`)
}
}
const tom = new Person('tom', 20);
// console.log(tom.age) // age为private变量,外部不可访问
class Student extends Person{
private constructor(name: string, age: number) {
super(name, age);
// console.log(this.age); // age为private变量,子类也不可访问
console.log(this.gender);
}
static create(name: string, age: number) {
return new Student(name, age);
}
}
// const jack = new Student('jack', 25); // 由于constructor是private私有变量,外部不可访问
const jack = Student.create('jack', 25); // 通过静态方法create去创建
11、类与接口、implements 关键字
// 单一职责原则,一个接口只定义一个功能
interface Eat {
eat(food: string): void
}
interface Run {
run(distance: number): void
}
// 使用implements关键字指定接口,多个接口使用 , 进行分割
class Person implements Eat, Run{
eat(food: string) {
console.log(`Person eat ${food}`);
}
run(distance: number) {
console.log(`Person run ${distance}`);
}
}
class Animal implements Eat, Run {
eat(food: string) {
console.log(`Animal eat ${food}`);
}
run(distance: number) {
console.log(`Animal run ${distance}`);
}
}
12、抽象类 abstract
使用 abstract 定义的类,只能被继承,不能被 new 创建实例对象;
父类中通过 abstract 定义的抽象,子类必须声明该方法。
abstract class Animal {
eat(food: string): void {
console.log(`Animal eat ${food}`)
}
// 有抽象方法时,子类必须声明该方法
abstract run(distance: number): void
}
// const newAnimal = new Animal(); // 报错,只能被继承,不能被 new 创建实例对象
class Dog extends Animal {
run(distance: number): void {
console.log(`Dog run ${distance}`);
}
}
13、泛型 <T>
定义函数、接口、类的时候没有指定具体的类型,调用的时候才去传递。
function createNumberArray(length: number, value: number): number[] {
const ary = Array<number>(length).fill(value);
return ary;
}
function createStringArray(length: number, value: string): string[] {
const ary = Array<string>(length).fill(value);
return ary;
}
// 以上代码有大量冗余,使用泛型解决
function createArray<T>(length: number, value: T): T[] {
const ary = Array<T>(length).fill(value);
return ary;
}
const res = createArray<string>(3, 'string');
const _res = createArray<number>(3, 10);
14、类型声明 declare
应用场景实例:由于引入的方法没有事先声明,导致 TypeScript 报错,可使用 declare 声明后再调用。
import { camelCase } from 'lodash';
declare function camelCase(input:string): string
const res = camelCase('hello ts')
// 此方案可通过使用 npm 安装“类型声明模块”替代