持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
TypeScript
- TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。
- TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。
安装
- 安装完之后,会提供
tsc命令。
npm install -g typescript
- 执行命令,编译ts文件会在同级目录下生成对应的js文件
tsc hello.ts
- 编译选项
--outDir 编译输出的目录
target ES版本,默认ES3
--watch 监听模式,文件发生改变自动编译
- 每次编译都输入这么多选项很麻烦,所以可以使用tsconfig.json配置文件。
tsc --init
执行上面的命令自动在当前目录下生成文件。
修改配置文件之后,可以直接使用tsc命令进行编译了。
tsconfig.json的配置:
{
"compilerOptions": {
"outDir": "./dist",
"target": "es2016"
},
// 解决提示的问题
"include": [ "./src/**/*" ]
}
类型系统
-
类型系统检测的是类型,而不是具体值。
-
类型标注,相当于把API文档集成到代码当中。为没有ts的框架或库提供类型系统的支持,更方便的代码提示。例如:百度地图的
baidumap-web-sdk
类型标注
基础类型
- string
- number
- boolean
let title: string = "哈哈哈";
let age: number = 18;
let isShow: boolean = true;
null 和 undefined
- 声明为
null或undefined类型后不可再修改。
let nul: null = null;
let undef: null = undefined;
// nul = 1; // 报错: 不能将1分配给类型null
- 默认情况下,
null和undefined是所有类型的子类型,所以下面的变量修改不会报错:
let a: string = "哈哈";
a = null;
a = undefined;
tsconfig.json中
strictNullChecks设置为true,可以检查代码中可能存在的有风险的null值。但如果有这个配置的话,上面的代码就会提示错误。
对象类型
- 下面的代码找不到
x,只能找到Object上的原型方法。因为这样声明是把obj1声明成了对象但并没有指定它的类型,所以在编译会报错,但在js中的话,也是可以找到x的。
let obj1: Object = {
x: 1,
y: 2
}
obj1.x; // 报错
obj1.toString();
对象字面量式类型标注
- 正确的声明:
let obj2: {x: number, y: number} = {
x: 1,
y: 2
}
obj2.x;
obj2.y;
内置对象类型
let d1: Date = new Date();
包装对象
StringBooleanNumber- 字符串
string是字符串对象new String()的一个子类。
let str1: string;
str1 = "哈哈哈";
str1 = new String("嘿嘿嘿"); // 报错
let str2: String;
str2 = new String("哈哈哈");
str2 = "嘿嘿嘿";
数组
- 两种声明方式:
let arr1: string[] = [];
arr1.push(100); // 错误
arr1.push("哈哈哈");
let arr2: Array<number> = [];
arr2.push(100);
arr2.push("哈哈哈"); // 错误
元组
- 与数组相似,但储存的数据类型多样
- 初始化个数与标注类型必须一致
- 后续添加的越界数据必须与标注的类型中任意一个一致,不校验顺序
let data1: [string, number] = ["哈哈", 1000];
data1.push(10);
data1.push("嘿嘿");
data1.push(true); // 错误
枚举
- 值只能是数字或字符串
enum HTTP_CODE {
OK = 200,
NOT_FOUND = 404
}
HTTP_CODE.OK; // 200
其他类型
无值类型 void
function fn(): void{
// return 1; // 错误
}
fn()
任意类型 any
- 任何值都可以赋值给
any类型 any类型也可以赋值给任何类型any类型有任意属性和方法- 相当于放弃了类型检测,也放弃了IDE的类型提示
let a: any = 1;
a = [1, 2, 4];
a = "haha";
a = 12;
a = true;
tsconfig配置:
"noImplicitAny": true,出现隐式的any时会报错。
未知类型 unknown
- 更安全的
any - 只能赋值给
unknownany - 没有任何属性和方法
函数类型
function foreach(data: string[], callback: (k: number, v: string) => void) {
for (let i: number = 0; i < data.length; i++) {
callback(i, data[i])
}
}
let arr = ["a", "b", "c"];
foreach(arr, function (k, v) {
})
接口 interface
- 接口只能作为类型使用,不能作为值去使用。
interface Point {
x: number,
y: number
}
let p: Point = {
x: 1,
y: 2
}
可选属性
- 用
?标识
interface Point {
x: number,
y: number,
color?: string
}
只读属性
- 用
readonly标识。 - 除了初始化以外,是不能被再次赋值的,只能读取。
interface Point2 {
readonly x: number
}
let p2: Point2 = {
x: 1000
}
p2.x; // 可访问
// p2.x = 2000; // 错误,不可修改
任意属性
- 表达式:
[prop: 索引类型]: 索引值类型。 [prop: string]: number表示可以向目标添加键为string类型、值为number类型的对象。
interface Point3 {
x: 1,
y: 2,
[prop: string]: number
}
let p3: Point3 = {
x: 1,
y: 2,
z: 3
}
p3.count = 10;
- 添加代码
p3[1] = 20;这样也不会出现错误,因为1最终会被转换为string类型。
注意事项
- 索引类型只能是
string和number之一。 - 但是两者同时出现时,要注意下面的问题。
interface Point4 {
[prop: string]: string,
// [prop1: number]: number, // 错误
[prop2: number]: string, // 正确
}
interface Point5 {
[prop: string]: Object,
// [prop1: number]: Date, // 正确
[prop2: number]: number, // 正确
}
- 如上:可以将索引的number类型理解为索引string类型的子类型(仅仅在索引这里可以这样理解),则索引number对应的值的类型必须和索引string对应的值的类型一致或者是其类型的子类型。
类型深入
联合类型
- 用符号
|表示
let a: number|string = 1;
a = "哈哈"
交叉类型
- 或者叫合并类型,用符号
&表示。 - 用于把多种类型合并在一起成为新的类型。
interface o1 {
x: 1,
y: 2
}
interface o2 {
z: 3
}
let obj1: o1 = {
x: 1,
y: 2
}
let obj2: o2 = {
z: 3
}
let obj3: o1 & o2 = Object.assign({}, obj1, obj2);
obj3.z;
字面量类型
let direction: 'left' | 'right' | 'top' | 'bottom';
// direction = "center"; // 错误
direction = 'left'; // 正确
direction = 'right'; // 正确
direction = 'top'; // 正确
direction = 'bottom'; // 正确
类型别名
- 用
type关键字定义 - 对象类型可以用上面说到的
interface,基本类型就可以用type
{
type dir = 'left' | 'right' | 'top' | 'bottom';
let direction: dir;
// direction = "center"; // 错误
direction = 'left'; // 正确
direction = 'right'; // 正确
direction = 'top'; // 正确
direction = 'bottom'; // 正确
}
类型推导
- 每次都显示标注类型比较麻烦,TypeScript提供了一种更加方便的特性:类型推导。
- TypeScript编译器会根据当前上下文自动的推导出对应的类型标注,这个过程发生在:
- 初始化变量
- 设置函数默认参数值
- 函数返回值
let num = 1; // 被类型推导识别为是number类型
// num = "哈哈"; // 错误,所以不能赋值给string类型
num = 100; // 正确
类型断言
- 断言表达式 1 :
< 类型 > - 断言表达式 2 :
as 类型
let img1 = <HTMLImageElement>document.querySelector("#img");
let img2 = document.querySelector("#img") as HTMLImageElement;
img1.src;
img2.src;
类型断言只是一种预判,不会对数据本身产生实际的作用。类似类型转换,但不会真实的转换类型。
类型操作符
typeof
- 操作值,抽取值的类型,后面只能接值,不能接类型。
typeof拿到的类型可以赋值给变量,也可以赋值给类型- 赋值给变量和类型,
typeof抽取出来的结果是不一样的
let colors = {
color1: "red",
color2: "blue",
}
// 赋值给变量
let a = typeof colors; // a 是联合类型 object | string | number | ...
// 赋值给类型
type b = typeof colors; // b -> { color1: string, color2: string }
let data: b; // data -> { color1: string, color2: string }
keyof
- 操作类型,获取类型所对应的类型key的集合,返回是key的联合类型。
interface Person {
name: string;
age: number
}
type t1 = keyof Person; // 相当于 type a = name | age
let data: t1;
data = "name";
data = "age";
// data = "a" // 报错
例子:
function css(ele: Element, attr: keyof CSSStyleDeclaration) {
return getComputedStyle(ele)[attr]
}
let box = document.querySelector(".box");
if (box) {
css(box, "width");
}
CSSStyleDeclaration接口 存在于lib.dom.d.ts类型声明文件中。
in
in操作符对值和类型都可以使用
// 对值的操作
console.log("name" in { name: "tom", age: 35 }); // boolean
// 对类型的操作
interface Person {
name: string,
age: number,
sex: boolean
}
type personKeys = keyof Person;
/**
* newPerson = {
* name: string,
* age: string,
* sex: string,
* }
*/
type newPerson = {
[k in personKeys]: string
}
- 原理: 内部使用
for ... in对类型进行遍历 in遍历完的因为要作为key使用,所以后面只能接number string Symbol
extends
- 类型继承
interface t1 {
x: number,
y: number,
}
interface t2 extends t1 {
z: string
}
let a: t2 = {
x: 1,
y: 2,
z: "kongcodes"
}
类型保护
- 下面代码,ts推断出arg的值可能是
string或array - 此处若使用
arg.push();会提示错误,因为push不是string和array的共有方法 arg.length就不会报错,因为length是共有的方法
function toUpperCase(arg: string | string[]) {
arg.push(); // 错误
arg.length; // 正确
}
- 可以使用类型保护解决类似的问题,ts提供了下面几个方法可以进行类型保护
typeof
- 使用了
typeof后会影响后续的类型推断,不会再报错
function toUpperCase(arg: string | string[]) {
if (typeof arg === "string") {
arg.toUpperCase();
} else {
arg.push();
}
}
instanceof
- 针对对象进行保护。
function toUpperCase(arg: string | string[]) {
if (arg instanceof Array) {
arg.push()
}
}
自定义类型保护函数
- 上面两种情况无法满足条件时
- 操作符
is
// 自定义类型保护
function canEach(data: Element | Element[] | NodeList): data is NodeList | Element[] {
return (<NodeList>data).forEach !== undefined;
}
function fn(elements: Element | Element[] | NodeList) {
if (canEach(elements)) {
elements.forEach(() => {});
}
}
// 举例调用
let eles = document.querySelectorAll(".box");
fn(eles);
上述代码:
(<NodeList>data)中的断言仅仅是为了解决提示错误的问题canEach返回布尔值为真的时候,说明data is NodeList | Element[]成立