学习内容:
typescript基础
- 基本数据类型
- 类型适配(断言)
- Interface接口
- class类
- 访问修饰符
- Generics泛型
- type 和 interface 异同
学习产出:
安装开发环境轻量级服务器
npm i -S lite-server
修改package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "lite-server" // 加入启动命令
},
启动项目,localhost:3000
npm start
ts的基础类型:string,number, boolean
ts中使用最频繁的类型就是字符、数字和布尔值。有一点要注意,这几个类型表述都是小写,千万不要跟 js当中的String, Number, Boolean混淆。
数组
常见的数组类型表达T[]和Array。
比如:
[1,2,3,4]一个数字集合的数组类型,我们可以用number[]及Array这样的语法来表示。
记住一点[number] 可不是数组类型,它是一个标准的 Tuples(元组)。
let list1: number[] = [1,2,3,4];
let list2: Array<number> = [1,2,3,4];
let list3 = [1,2,3,4];
let list4 = [1, "a"]; // 只能存放string,number
let list5:any[] = [1, "a", true]; // 可以存放任意类型
元组 Tupple
固定长度、固定类型的数组
声明时必须指定类型,否则就是一个联合类型的数组,不是元组
let tup1: [number, string] = [1, "a"];
console.log(tup1);
tup1[0] = 2;
tup1[1] = "b";
tup1.push(1); // 元组的bug
console.log(tup1);
联合(Union)类型
指定类型
let union1: string | number | string[];
union1 = 1;
union1 = "a";
// union1 = [1,3]; // 编译报错
指定取值范围
let union2: 1 | 2 | false;
union2 = 1;
union2 = false;
字面量(Literal)类型
let lite1: 1 | "a" | true | [1, 3];
在变量声明的同时指定其类型
定义方式——变量:类型
let myName: string = "Alice";
ts有个特性:类型推断。如上例子也可以直接:
let myName = "Alice";
ts也会根据赋值类型,推断出来变量的类型。
关于标量是否需要指定其类型,官方说:在定义变量的时候,尽可能的少指定其类型。
枚举类型(Enum)
enum Color {
red = "a",
blue = 3,
yellow = "2"
}
let color1 = Color.red;
let color2 = Color.blue;
let color3 = Color.yellow;
console.log(color1, color2, color3);
枚举是TypeScript为数不多的特性之一,它不是JavaScript的类型级扩展。
1. Numeric enums(数值枚举)
enum Direction {
Up = 1,
Down,
Left,
Right,
}
enum Direction {
Up,
Down,
Left,
Right,
}
数值枚举,默认的起始数值是1,后续以此递增。如果指定了头部值,那么默认值就会变成指定值。
2. String enums(字符枚举)
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
枚举常用在字面化表达一个变量的不同类型值,比如:性别。
any动态类型
如果设置了某个变量的类型为any类型,那么不单单是这个变量本身类型不会被检测,它的属性和方法也会绕过ts的类型检测。
let rand:any = 1;
rand = true;
rand = {};
// rand(); // 编译通过,执行js才报错
unknown
let rand2:unknown = 1;
// rand2(); // 编译报错
if(typeof rand2 === "function")
rand2();
void 变量本身就不存在,undefined变量未赋值
never 永远没有返回值
一般用来处理异常
function throwError(message: string, errorCode: number): never {
throw{
message,
errorCode
}
}
throwError("not found", 404);
类型适配(类型断言)Type Assertions
let message: any;
message = "abc";
message.endsWith("c"); // 编辑器无提示
// 两种方式
let re1 = (<string>message).endsWith("c");
let re2 = (message as string).endsWith("c");
ts的非undefined和null的断言修饰符“!”)
TypeScript还有一种特殊语法,用于在不进行任何显式检查的情况下从类型中删除null和undefined。具体实例如下:
function liveDangerously(x?: number | null) {
console.log(x.toFixed());
//Object is possibly 'null' or 'undefined'
console.log(x!.toFixed());
}
函数类型
可选参数和默认值只能放在最后一个
// code: number = 1
let log = (message: string, code?: number): string => {
return `${message}${code}!!`;
}
log("雷猴");
object对象类型
js中是key to value 键值对
ts中是key to type 键类型对
将所有的对象属性一一列出就是对象类型的基础用法。
参数可选:
function printCoord(pt: { x: number; y?: number }) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });
Interface接口
let drawPoint = (point: Point) => {
const { x, y } = point;
console.log(x, y);
}
drawPoint({x:100, y:120});
// drawPoint({x:"a", y:"b"});// 编译报错
interface Point {
x: number,
y: number
}
Class类
以下代码中getX和setX使用getter setter懒人包示范,编译时报错Accessors are only available when targeting ECMAScript 5 and higher.
使用命令tsc -t es5 demo.js
指定js版本即可。
// IPoint 表示一个Interface
interface IPoint {
// class类中使用private修饰的变量,在接口中应该去掉
// x: number;
// y: number;
drawPoint: ()=>void;
getDistances: (p: IPoint) => number;
X: number; // getter setter懒人包
// getX: () => number;
// setX: (value) => void;
getY: () => number;
setY: (value) => void;
}
接口实现使用关键词implements
class类中要实现IPoint中所有函数
js不可重载,有且仅有一个 constructor,可选参数和设置参数默认值可以变相达到重载效果
y?: number
表示可选参数
访问修饰符public(默认), private, protected
,在声明构造函数的同时顺便完成成员变量的声明
class Point implements IPoint {
// 成员变量
// x: number;
// y: number;
constructor(private x: number, private y: number) {
// this.x = x;
// this.y = y;
}
// 成员函数
drawPoint = () => {
console.log("x: ", this.x, ", y: ", this.y);
};
getDistances = (p: IPoint) => {
return Math.pow(p.X-this.x, 2) + Math.pow(p.getY()-this.y, 2);
};
// 缓冲带,避免一些非法输入
set X(value: number) {
if(value < 0) {
throw new Error("value不能小于0");
}
this.x = value;
};
get X() {
return this.x;
};
setY = (value: number) => {
if(value < 0) {
throw new Error("value不能小于0");
}
this.y = value;
};
getY = () => {
return this.y;
};
}
const point = new Point(3, 4);
// point.x = 2;// Property 'x' is private and only accessible within class 'Point'.
// point.y = 3;
// point.setX(-19);
// point.setX(19);
// console.log(point.getX());
point.X = 18;
console.log("point.X=", point.X); // 18
point.drawPoint();
Generics 泛型
其中输入的类型与输出的类型相关,或者两个输入的类型以某种方式相关。
let lastInArray = <T>(arr:T[]) => {
return arr[arr.length-1];
}
// 鼠标放在下列const声明的变量上可以看到变量的类型
const l1 = lastInArray([1, 2, 4]); // number
const l2 = lastInArray(["a", "b", "c"]); //string
const l3 = lastInArray<string | number>([1, "a", "b"]); // string | number
// 指定默认类型
let makeTuple = <T, Y = number>(x: T, y: Y) => [x, y];
// 可以指定类型,不指定时js会动态匹配类型
const v1 = makeTuple(1, "a");
const v2 = makeTuple<boolean>(true, 1);
不常用的类型
从ES2020开始,JavaScript中有一个用于非常大整数的原语BigInt:
// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;
JavaScript中有一个原语,用于通过函数Symbol()创建全局唯一引用:
const firstName = Symbol("name");
const secondName = Symbol("name");
if (firstName === secondName) {
This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.
// Can't ever happen
}
Narrowing(类型窄化/过筛)
in操作符
当一个属性为可选字段的时候,用in判断,它会出现在true和false的两边。
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
type 和 interface:
区别:
- 接口 interface 只能用于定义对象类型。
- 类型别名 type 可以用来给一个类型起个新名字,当命名基本类型或联合类型等非对象类型时非常有用。
- 同名接口会自动合并,而类型别名不会。
相同点:
- 类型别名和接口都可以用来描述对象或函数
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}
- 类型别名和接口都支持扩展: 类型别名通过 &(交叉运算符)来扩展,而接口通过 extends 的方式来扩展。
// 类型别名扩展
type Animal = {
name: string
}
type Bear = Animal & {
honey: boolean
}
const bear: Bear = getBear()
bear.name
bear.honey
// 接口扩展
interface Animal {
name: string
}
interface Bear extends Animal {
honey: boolean
}
// 此外,接口也可以通过 extends 来扩展类型别名定义的类型:
type Animal = {
name: string
}
interface Bear extends Animal {
honey: boolean
}
// 同样,类型别名也可以通过 &(交叉运算符)来扩展已定义的接口类型:
interface Animal {
name: string
}
type Bear = Animal & {
honey: boolean
}
使用场景
- 使用类型别名的场景:
定义基本类型的别名
定义元组类型
定义函数类型
定义联合类型
定义映射类型
- 使用接口的场景:
需要利用接口自动合并特性时问
定义对象类型且无需使用type时