1、安装TypeScript
npm install -g typescript//安装
tsc -v//查看安装版本
tsc (要编译的文件)//将ts文件转换为js文件
node (被编译js文件)//执行文件
2、tsc编译
tsc (要编译的文件名)
编译完的js文件不存在变量类型
ts; //编译前
let test: number = 18;
js; //编译后
var test = 18;
3、安装ts-node包
npm install -g ts-node//安装
ts-node (ts文件名)//运行文件
4、TS常用类型
TypeScript是js的超集,TS提供了JS的所有功能,并且额外的增加了:类型系统。
注:JS不会检查变量的类型是否发生变化,而TS会检查
4.1、类型注解
let test: number = 18; //类型注解
4.2、常用基础类型
可以将TS中的常用基础类型细分为两类
-
JS原有类型
原始类型:number/string /boolearn/null/undefind/symbol
let test: number = 18; //数字类型
let test1: string = '18'; //字符串类型
let test2: boolean = true; //布尔类型
let test3: null = null; //空
let test4: undefined = undefined; //undefinf类型
let test5: symbol = Symbol(); //symbol类型
对象类型:object(包括数组、对象、函数对象)
数组:
let test: number[] = [1, 2, 3]; // (推荐)
let test: Array<string> = ['a', 'b', 'c'];
let test: (number | string)[] = [1, 'k', 'c', 5]; //(数组中有多种类型)
-
TS新增类型
联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any等
4.3、联合类型
let Test: number | string; //number或string
4.4、类型别名
简述:为任意类型起名
关键字:type
用途:一种复杂类型被多次使用时可以通过类型别名替换使用。例如
type CustomArray = (number | string)[];
let text: CustomArray = [1, 'v', 'c', 5];
4.5、函数类型
函数类型就是为函数的参数和返回值添加类型
为函数指定类型方式有两种
1.单独指定参数、返回值类型
function test(num1: number, num2: number): number { //普通函数
return num1 + num2;
}
const test = (num1: number, num2: number): number => { //箭头函数
return num1 + num2;
};
2、同时指定指定参数、返回值类型
const test: (num1: number, num2: number) => number = (num1, num2) => { //指定
return num1 + num2;
};
注:这种方式只适用于函数表达式
void类型
function test(name: string): void { //没有返回值的函数为void类型
console.log(name);
}
传递可选参数
function test(num?: number): void { //可选参数就是在参数的后面+?
console.log(num);
}
4.6、对象类型
JS中的对象是由属性和方法构成的,而TS的对象实在描述对象的机构
写法:
let test: { name: string; age: number; play(): void; playl(item: number): void } = {
name: 'lisi',
age: 15,
play() {},
playl(item: 1) {},
};
竖排情况下可以去掉分号
let test: {
name: string;
age: number;
play(): void;
playl(item: number): void;
} = {
name: 'lisi',
age: 15,
play() {},
playl(item: 1) {},
};
对象作为参数传递时属性也可以实现选择传输
function test(obj: { name: string; age?: number }): void { //对象的属性实现可选同样+?
console.log(obj);
}
4.7、接口
使用interface关键字声明接口(只用于声明属性一起属性类型)
interface Test { //声明接口
name: string;
age: number;
play(): void;
}
使用时只需要调用接口
let test: Test = {
name: 'lisi',
age: 15,
play() {},
};
注:接口只能为对象声明变量类型(前面学到的type类型别名 可以对多种类型指定类型
如果两个接口之间有相同的属性或方法,可以将公共得到属性抽离出来,通过继承来实现复用,关键字(extends)
interface Test1 {
name: string;
}
interface Test2 {
name: string;
age: number;
}
两者的效果一样
interface Test1 {
name: string;
}
interface Test2 extends Test1 {
age: number;
}
4.8、元组
使用场景:在地图中标注使用经纬度标注信息(以及确切的知道包含多少元素,以及特定索引对应的类型)
可以用数组来标注,数组中只有两个元素,并且都是数值类型
let test: number[] = [32.25211, 12.5665]; //可以但是不推荐(数组中可能会出现多个元素)
let test: [number, number] = [32.25211, 12.5665]; //推荐
4.9、类型推论
在TS中某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型例如:
1、声明变量\textcolor{Red}{并且}初始化值的时候
let test = 18; //ts会自动判断test的类型时number类型,鼠标放在上面会看到
let test; //知声明并没有初始化值的时候建议写法如下
let test: number;
2、决定函数返回值的时候
function test(num1: number, num2: number) {
return num1 + num2;
}
4.10、类型断言
当你特别清楚要是用什么类型的时候采用类型断言
let test = HTMLElement; //(类型推论机制符合第一条,可以不写)
let test = document.getElementById('id');
getElementById方法返回值时HTMLElement该类型包含所有标签公共属性或方法,不包含a标签特有的href等属性
使用类型断言来指定更加具体的类型
let test = document.getElementById('id') as HTMLAnchorElement; //推荐
let test = <HTMLAnchorElement>document.getElementById('id'); //不常用知道即可
HTMLAnchorElement时A标签的类型,也是HTMLElement的子类型
查看元素类型通过浏览器控制台:console.dir()
4.11、字面量类型
使用场景:一般用于一组明确可选列表
let test = 'hello'; //类型为string
const test = 'hello'; //类型为'hello'(const定义常量值不能变,所以类型也为hello)
let test: 12 = 12; //
字面量类型配合联合类型一起使用
function map(direction: 'up' | 'down' | 'left' | 'right') { //只能从上下左右四个方向选择一个,更加的精准
console.log(direction);
}
4.12、枚举
类似字面量 + 联合类型的组合的功能
enum Test { //定义
Up,
Down,
Left,
Right,
}
function test(direction: Test) {
console.log(direction);
}
1、关键字 enum
2、约束枚举名称和枚举元素都首字母大写
3、枚举多个值用逗号隔开
4、定义枚举后直接使用枚举作为类型注解
5、枚举元素如若未定义值默认从零开始一次加1,这样的称(数字枚举)也可以给元素赋值
enum Test {
Up = 10,
Down,
Left,
Right,
} //从10开始,依次是10,11,12,13
enum Test {
Up = 2,
Down = 4,
Left = 6,
Right = 8,
} //
6、字符串枚举(注:字符串枚举子增长行为,字符串枚举每个元素必须有值)
enum Test {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right',
} //
枚举与字面量类型+联合类型相比,推荐字面量类型+联合类型,因为:编译为js时字面量类型+联合类型会被直接删除,而枚举则会编译为js代码,降低性能
4.13、any(不推荐了解即可)
let test: any = { test: 0 }; //你可以对test做任意操作但是ts不会给出任何提示
test.test1 = 100; //
test(); //
const test2: number = test; //
1、any类型声明变量不提供类型也不提供默认值,类似any类型
2、函数参数不加类型,也类似any类型
4.14、typeof获取数据类型
console.log(typeof 'hello'); //string
ts中typeof可以用来查询已有变量的类型,例如
let p = { x: number, y: number };
function test(test: { x: number; y: number }) {} //
function test(test: typeof p) {} //简化写法,效果一样,(推荐)
let num: typeof p.x; //拿到p中的x的元素类型
注:只用来查询变量或属性的类型,无法查询其他形式的类型(比如:函数调用的类型)
5、TypeScript高级类型
TS中的高级类型有很多,重点学习以下高级类型:
1、class类
2、类型兼容性
3、交叉兼容性
4、泛型和keyof
5、索引签名类型和索引查询类型
6、映射类型
5.1、class类
TS全面支持ES5引入的class关键字,还增添了类型注解和其他语法(比如可见性修饰符)
class Person {
age: number;
name: string;
constructor(age: number, name: string) {
this.age = age;
this.name = name;
}
} //class类
const p = new Person(18, '张三'); //初始化
函数是需要返回值类型的,否则会被推断为any类型;构造函数不需要返回值类型
实例方法
class Person {
age: 18;
play(age: number): void {
console.log((this.age *= age));
}
} //class类
const p = new Person();
class类继承,1、extends(继承父类)2、implements(实现接口)
说明:JS中只有extends,而implement是TS提供的
extends继承
class Person {
move() {
console.log('moving');
}
}
class Dog extends Person { //通过extends继承Person类的move方法
run() {
console.log('running');
}
}
const D = new Dog();
D.move(); //moving
implements(实现接口)
interface Test {
name: string;
play(): void;
}
class Person implements Test { //Person类通过implements实现接口时,必须提供接口中的所有属性和方法
name: 'zhangsan';
play() {
console.log('play');
}
}
类成员的可见性:可以使用TS来控制class的方法和属性是否可见。
可见性修饰包括:1、public(共有的)2、protected(受保护的)3、private(私有的)
1、public:表示公开,共有,都可以访问的,(默认public)
class Person {
public plsy() {} //默认public可以不写
}
2、protected:表示受保护的,仅对其生命所在和子类中(非实例对象可见)
class Person {
protected play() {}
}
class People extends Person {
test() {
this.play();
}
}
3、private:表示私有的,只有在当前类之中可见
class Person {
private study() {}
read() {
this.study();
}
}
只读修饰符readonly:表示只能读取,不能修改
注:只可以修饰属性,不可以修饰方法,
class Person {
readonly name = 'zhangsan'; //字面量类型,zhangsan
readonly name: string = 'zhangsan'; //string类型
constructor(name: string) {
this.name = name;
}
}
接口或{}表示对象类型,也可以用readonly
interface Test {
readonly name: string;
}
let test: Test = {
name: 'jack',
};
let test: { readonly name: string } = {
name: 'jack',
};
5.2、类型兼容性
两种类型系统:
1、Structural Type System(结构化类型系统)2、Nominal Type System(标明类型系统)
TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具有的形状。
也就是说,如果两个对象的具有相同的形状,则认为他们是同一类型
原则:属性多的兼容属性少的(参数少的可以赋值给参数多的)
class Test1 {
name: string;
age: number;
}
class Test2 {
name: string;
age: number;
}
let test3: Test1 = new Test2(); //TS是结构化类型系统,只检查Test1和Test2的结构是否相同
在结构化类型系统中,如果说两个对象拒用相同的结构,则认为他们是同一类型,这种说法并不准确
更为准确的说法是:对于对象类型来说属性多的可以兼容属性少的(前提:属性多的要完完全全的包含属性少的所有属性)
class Test1 {
name: string;
age: number;
}
class Test2 {
name: string;
age: number;
height: number;
} //Test2包括Test1的所有属性
let p: Test1 = new Test2(); //Test2的属性比Test1的属性多(属性多的兼容属性少的)解析如下
let p = {
name: 'string',
age: 2,
}
接口兼容性与对象兼容性类型(属性多的兼容属性少的)
interface Test1 {
name: string;
age: number;
}
interface Test2 {
name: string;
age: number;
height: number;
}
let case1: Test1;
let case2: Test2;
case1 = case2; //Test2的属性比Test1的属性多(属性多的兼容属性少的)
class与interface之间也可以兼容,原则:属性多的兼容属性少的
interface Test1 {
name: string;
age: number;
}
class Test2 {
name: string;
age: number;
height: number;
}
let p: Test1 = new Test2();
函数类型兼容性比较复杂,影响因素有多个,例如
1、参数的个数:参数多的兼容参数少的
type Test1 = (name: string, age: number) => void;
type Test2 = (name: string, age: number, height: number) => void;
let case1: Test1;
let case2: Test2;
case2 = case1;
2、参数的类型:相同位置的参数类型要相同(原始类型)或兼容(对象类型或接口类型)
相同
type Test1 = (name: string, age: number) => void;
type Test2 = (name: string, age: number) => void;
let case1: Test1;
let case2: Test2;
case2 = case1;
case1 = case2;
兼容
interface Test1 {
name: string;
age: number;
}
interface Test2 {
name: string;
age: number;
height: number;
}
type Case1 = (test: Test1) => void;
type Case2 = (test: Test2) => void;
let case1: Case1;
let case2: Case2;
case2 = case1;
3、函数的返回值类型
1、如果是原始类型,相同即可
type Test1 = () => void;
type Test2 = () => void;
let case1: Test1;
let case2: Test2;
case2 = case1;
case1 = case2;
2、如果对象类型,兼容即可
type Test1 = ({ name: string, age: number }) => void;
type Test2 = ({ name: string, age: number, height: number }) => void;
let case1: Test1;
let case2: Test2;
case2 = case1;
5.3、交叉类型
交叉类型(&)类似接口继承(extends)用于组合多种类型(常用于对象类型)
interface Test1 {
name: string;
age: number;
}
interface Test2 {
name: string;
age: number;
}
type Test3 = Test1 & Test2; //Test3同时具备了Test1和Test2的所有属性
交叉类型与接口继承不同点
如果实现组合是有同名属性,继承会直接报错,交叉会将两种类型采用并集的方式处理,请看下面代码
interface Test1 {
fn(value: number): void;
}
interface Test2 extends Test1 { //会直接报错
fn(value: string): void;
}
interface Test1 {
fn(value: number): void;
}
interface Test2 {
fn(value: string): void;
}
type Test3 = Test1 & Test2; //相当于下面代码
interface Test3 {
fn(value: string): void; //做并集处理采用或的方式进行处理要么选择第一个要么选择第二个
fn(value: number): void; //两个选其一
}
interface Test1 {
fn(value: number): void;
}
interface Test2 {
fn(age: number): string;
}
type Test3 = Test1 & Test2; //相当于下面代码
let p: Test3 = {
fn(value: number) {}, //做并集处理采用或的方式进行处理要么选择第一个要么选择第二个
fn(age: number) { //两个选其一
return '';
},
};
5.4、泛型
泛型是在可以保证类型安全的前提下,让函数与多种类型一起工作,从而实现服用,常用于函数、接口、class中。
需求:传入一个数据,传入什么类型就以什么类型输出(参数与返回值类型相同)语法:<>
function Passvalue<Type>(value: Type): Type { //类型变量
return value;
}
let i = Passvalue<number>(10); //调用传变量名和值
let i = Passvalue<string>('10');
function Passvalue<Type>(value: Type): Type { //类型变量
return value;
}
let i = Passvalue(10); //TS会采用参数类型推断机制来判断Type的类型(简单写法)
let i = Passvalue('10');
泛型约束:默认情况下,泛型函数中的类型变量可以代表多种变量,这导致无法访问任何属性 ,这时就需要泛型约束(缩小类型的取值范围)
指定类型约束
function Passvalue<Type>(value: Type[] | ''): Type[] | '' {
console.log(value.length);
return value;
}
let i = Passvalue([]);
let i = Passvalue('');
extends添加约束
interface Test {
length: number;
}
function Passvalue<Type extends Test>(value: Type): Type {
console.log(value.length);
return value;
}
let i = Passvalue([]);
let i = Passvalue('');
泛型有多个类型变量
function Passvalue<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
Passvalue({ name: 'lisi', age: 18 }, 'name');
Passvalue(1, (可以访问对应的方法例如tostring等等));
Passvalue('abcd', (可以访问对应的方法例如tostring等等));//当输入数字时则是对应的索引,当输入高于字符串长的值时则输出undefind
keyof关键字接收一个对象类型,生成其键名称(可能时字符串或数字)的联合类型
key的值被限制在Type的所有键之一,或只能访问对象中存在的属性
泛型接口:接口也可以配合泛型来使用,增加其灵活性,增强复用性
interface Test<Type> { //接口的类型变量,对接口的其他成员可见,所有成员都可以使用
fn(value: Type): Type;
}
let Test1: Test<number> = { //使用泛型接口时,需要显示指定的具体类型
fn(value) {
return value;
},
};
泛型数组:JS中的数组在TS中就是一个泛型接口
const str: Array<string> = ['a', 'b', 'c']; //类似接口写法Arrey接口名称<string>类型变量
泛型类:class也可以配合泛型来使用
class Test<Type> { //泛型类类似泛型接口,同样在类名称之后添加<>
name: Type;
add: (age: Type, height: Type) => Type;
}
泛型工具类型:都是基于泛型实现的,因为是内置,可以直接使用
1、Partial<Type>:用来创建一个类型,将Type的所有属性设置为可选
interface Person {
name: string;
age: number;
}
type Man = Partial<Person>; //解析为下面代码
interface Man {
name?: string;
age?: number;
}
2、Readonly<Type>:用来构造一个类型,将Type的所欲类型都设为Readonly(只读)
interface Person {
name: string;
age: number;
}
type Man = Readonly<Person>; //解析为下面代码
interface Man {
readonly name: string;
readonly age: number;
}
3、Pick<Type,Keys>从Type中选择一组数据来构造新类型
interface Person {
name: string;
age: number;
height: number;
}
type Man = Pick<Person, 'name' | 'age'>; //解析为下面代码
interface Man {
name: string;
age: number;
}
4、Record<Keys,Type>构建一个对象类型,属性键为Keys,属性为Type(Keys为元素,Type为元素的类型)
type Man = Record<'name' | 'age', string>; //解析为下面代码
type Man = {
name: string;
age: string;
};
5.5、索引签名类型
索引签名类型:当无法确定对象中有那些属性时,采用索引签名类型。
interface Test {
[key: string]: number; //接口内的元素名称的类型和元素值的类型
}
let Test1: Test = {
a: 2,
};
注:所有的类型做都都将转换string类型,所以string类型可以代替所有类型
interface Test<Type> {
[key: number]: Type; //接口内的元素名称的类型和元素值的类型
}
let Test1: Test<number> = [1, 3, 5];
数组情况
5.6、映射类型
映射类型:通俗来讲就是将一个存有多个key的集合,把每一个属性都复制到新对象中并设置属性
联合类型创建新类型:将联合类型内的所有元素复制到新类型中,并且为所有属性设置类型
type Keys = 'x' | 'y' | 'z'; //多个Key的集合
type Test = {
[key in Keys]: number; //复制并设置属性
}; //解构如下
type Test = {
x: number;
y: number;
z: number;
};
对象类型创建新类型:将对象类型内的所有元素复制到新类型中,并且所有属性的类型会被重新设置
type Keys = {
name: string;
age: number;
height: number;
};
type Test = {
[key in keyof Keys]: number; //复制并设置属性
}; //解构如下
type Test = {
name: number;
age: number;
height: number;
};
let h: Test = { //调用
name: 1,
age: 1,
height: 1,
};
泛型工具类型都是基于映射类型实现的
例如Partial<Type>的实现:
type Partial<Type> = {
[key in keyof Type]?: Type[key]; //拿到每个属性以及对应的类型
}; //上面代码理解即可
type Test = {
x: number;
y: number;
z: number;
};
type Test1 = Partial<Test>; //Partial是内置直接使用即可
let h: Test1 = {
//调用,Partial是将传输的类型中的属性全部变为可选择传输,所以即使没有z属性也不会报错
x: 1,
y: 2,
};
解释:使用Partial<传输一个对象类型或其他类型>,将里面所有的元素以及对应的属性一起复制到新类型之中
Type[key]这种语法叫做索引查询(访问)类型
作用:查询属性的类型
type Test = {
x: number;
y: string;
z: boolean;
};
let test1: Test['x']; //拿到Test中x的类型并设置test1为同样的类型
type test3 = Test['x' | 'y']; //同时拿到多个类型,解析如下
type test3 = number | string;
type test3 = Test[keyof Test]; //拿到所有类型,解析如下
type test3 = number | string | boolean;
6、TypeScript类型声明文件
作用:为已存在的JS库提供类型信息
6.1、TS中的两种文件类型
1、.ts文件
既包含类型信息又包含可执行代码
可以被编译为.js文件,执行代码
代码编写的地方
2、.d.ts文件
只包含类型信息的类型声明文件
不会生成.js文件,仅用于提供类型信息
为js提供类型信息
6.2、类型声明文件的使用说明
学习顺序:先会用(别人的),再会写(自己的)
1、使用已有的类型声明文件
(1)内置类型声明文件:TS为JS运行时可用的所有标准化内置API都提供了声明文件
可以通过ctrl+单机来查看内置类型声明文件内容
(2)第三方库的类型声明文件
1.库自带类型声明文件
2.由DefinitelyTyped提供:是一个Git仓库用来提供高质量TypeScript类型声明
2、创建自己的类型声明文件
(1)项目内共享类型
1.创建index.d.ts类型声明文件
2.创建需要共享的类型,并使用export导出(TS中也可以使用import/export实现模块化功能)
3.在需要使用共享类型的.ts文件中,通过import导入即可(.d.ts文件导入时,直接省略后缀即可)
(2)为已有的js文件提供类型声明
关键字(declare):为已存在的变量提供类型
js文件
let test = 18;
ts文件
declare let test: number;
注:对于type、interface等明确就是ts类型的,可以省略declare,对于let、function等具有双重含义的应该使用declare