一.安装ts
npm i typescript -g
获取命令tsc tsc --version 查看版本
二.创建项目
npm init -y
tsc --init 生成文件tsconfig.json
npm i @types/node -S
三.ts编译成js
##vscode方法
终端——运行任务——显示所有任务——tsc构建-tsconfig.json 生成对应js文件
终端——运行任务——显示所有任务——ts监视-tsconfig.json 实时生成js文件
##package.json脚本方法
```
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"build:watch": "tsc --watch"
},
```
四.数据类型
1.一般类型
如果代码中有export import 之类代码,那么这个文件会变成一个模块,变量就不是全局变量
export {}
let name: string = 'zs';
let age: number = 18;
let married: boolean = true;
let hobbies: string[] = ['1','2','3'];
let interests: Array<number> = [4,5,6]
2.元组 类似一个数组 类型和长度都固定的数组
// 特点: 1.长度固定2.类型可以不一样
let point: [number, number] = [3,5];
console.log(point[0],point[1]);
let person: [number, string] = [23, 'zs']
3.枚举类型
enum Gender {
BOY,
GIRL
}
// let Gender = {
// 0: 'BOY',
// 1: 'GIRL',
// GIRL: 1,
// BOY: 0
// }
console.log(`zs是一个${Gender.BOY}`) // zs是一个0
console.log(`ll是一个${Gender.GIRL}`) // ll是一个1
enum week {
MONDAY = 5,
TUESDAY = 2,
}
// let Gender = {
// 5: 'MONDAY',
// 2: 'TUESDAY',
// TUESDAY: 2,
// MONDAY: 5
// }
4.常数枚举
const enum Colors {
Red,
Green,
Blue
}
console.log(Colors.Red,Colors.Green,Colors.Blue) //0 1 2
5.任意类型 anyscript
let root: any = document.getElementById('root');
root.style.color = 'red';
// let root = document.getElementById('root');
// ts 为dom提供了一整套类型声明
// let root: HTMLElement | null = document.getElementById('root');
// root!.style.color = 'red'; // ! 强行断言 断言不为空
6.undefined null未定义
// undefined 未定义
// null 空
// 他们都是其他类型的子类行,可以把他们赋值给其他类型的变量 (严格模式下不可以赋值,tsconfig.json文件中"strictNullChecks": false,)
let myname: string = null;
let myname1: string = undefined;
7.void类型 空的
function greeting(name: string): void{ // 不能又返回值 可以返回null,undefined
console.log('hello')
}
8.never 永远不 其他类型的子类型 代表不会出现的值
// 在函数内部永远会抛出错误,导致函数无法正常结束
function cteateErr(msg: string): never {
console.log(1);
throw new Error(msg);
console.log(2) // 永远不会执行
}
function sum(a: number): never {
while(true) {
console.log(1)
}
console.log(2) // 永远不会执行
}
9.类型推论
// 推论 猜
let num1 = 2;
num1 = 4;
num1 = '3' //报错,推论number类型
let num; // 推论any类型 一旦推论出类型,不能改变
num = 2;
num = '3'
10.包装对象 (自动在基本类型和对象类型之间切换) java 装箱和拆箱
// 基本类型上没有方法
// 2.在内部迅速的完成一个装箱操作,把基本类型迅速包装成对象类型,然后用对象来调用方法
let name4: string = 'zs';
name4.toLocaleLowerCase(); // 有方法
let name44 = new String(name4);
name44.toLocaleLowerCase();
let isok1: boolean = true;
let isok2: boolean = Boolean('xx');
let isok3: boolean = new Boolean(1); //报错 new Boolean()是对象,不能赋值
11.联合类型
let name5: string | number;
name5 // 不能调用string和number的单独方法,可以调用通用方法
name5 = 'zs'; // 可以调用sting的方法
name5 = 13; // 可以调用number的方法
12.断言类型
let name6: string | number;
(name6 as string).toLocaleLowerCase();
(name6 as number).toFixed(2);
(name6 as boolean).toFixed(2);// 不包含boolean,会报错
13.字面量类型
let Gender2: 'Boy' | 'Girl';
Gender2 = 'Boy';
Gender2 = 'Girl';
Gender2 = 'G'; // 报错,只能赋Girl或Boy
五. 函数
1.函数定义
// : void 返回类型
function hello(name: string): void {
console.log('hello' + name);
}
2.函数表达式
// type 定义一个类型或者类型别名
// (firstName: string, lastName:string) 参数类型
// { name: string } 函数返回类型
type GetUserNameType = (firstName: string, lastName:string) => void;
let getuserName: GetUserNameType = function (fistName: string, lastName: string): void {
}
type GetUserNameType = (firstName: string, lastName:string) => {
name: string
}
let getuserName: GetUserNameType = function (fistName: string, lastName: string): {
name: string
} {
return { name: fistName + lastName}
}
3.可选参数
function print(name: string, age: number, home: string) {
}
print() // 参数要么三个要么不传,不然就报错
// 参数加上?表示可传可不穿
function print(name: string, age?: number, home?: string) {
}
print('zs')
print('zs',10)
print('zs',10,'shh')
// print('zs',10,'shh','df')// 不可以传四个
4.默认参数
// method: string = 'GET' 调用时可以不传
function ajax(url: string, method: string = 'GET') {
console.log(url, method)
}
ajax('/user') // user GET
ajax('/user', 'POST') // user POST
5.剩余参数
function sum1 (...numbers:Array<number>) {
return numbers.reduce((accu, item) => accu + item, 1)
}
console.log(sum1(1,2,3)) //7 1+1+2+3
6.函数重载
// 重载的定义和函数的声明要放在一起
let obj: any = {};
function attr(val: string): void; // 重载的定义
function attr(val: number): void; // 重载的定义
function attr(val: any): void { // 函数的声明
if(typeof val === 'string') {
obj.name = val;
} else if(typeof val === 'number') {
obj.age = val
}
}
attr('zs')
attr(10)
// attr(true) // 报错
console.log(obj)
// a和b要么都是字符串要么都是数字
function sum2(a: string,b: string): void;
function sum2(a: number,b: number): void;
function sum2(a: any,b: any): void {
return a + b;
}
sum2(1,2)
sum2('1','2')
// sum2('1',2) // 报错
6.箭头函数
type DelayType = (ms: number) => void;
let delay: DelayType = (ms: number) => {
setTimeout(function(){
}, ms)
}
六. 类
1.如何定义类
// 需要初始化值 name: string = 'zs';this.age = 10;
class Person {
name: string = 'zs';
age: number;
constructor() {
this.age = 10;
}
}
let p1 = new Person()
console.log(p1)
2.存储器 getter setter
class Person {
myName: string;
constructor(name: string) {
this.myName = name;
}
get name() { // get name
return this.myName;
}
set name(newVal: string) { //set name
this.myName = newVal.toLocaleUpperCase()
}
}
let p1 = new Person('zs')
console.log(p1)
p1.name = 'ls';
console.log(p1)
3.参数属性
class Person {
myName: string;
constructor(name: string) {
this.myName = name;
}
}
class Person {
constructor(public name: string) {
}
}
let p1 = new Person('naue')
console.log(p1)
4.readonly
class Person {
constructor(public readonly name: string) {
}
}
let p1 = new Person('naue1')
p1.name = 'js'; // 报错 readonly
console.log(p1)
5.继承
6.类里的修饰符
// 子类继承父类后子类的实例上就有了父类的属性和方法
// 访问修饰符 public protected private
// public 自己 自己的子类 和其他类都能访问 默认
// protected 受保护的 自己 自己的子类能访问,其他类不能访问
// private 只能自己访问
class Person {
public name: string;
protected age: number;
private amount: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age
this.amount = 100
}
getName() {
return this.name;
}
setName(newVal: string) {
this.name = newVal
}
}
class Student extends Person {
stuNo: number;
constructor(name: string,age: number,stuNo: number) {
super(name, age);
this.stuNo = stuNo;
}
getStuNo() {
return this.stuNo + this.amount; //this.amount私有属性,子类不能访问
}
setStuNo(newVal: number) {
this.stuNo = newVal
}
}
let stu = new Student('zs',23,12345)
console.log(stu.getStuNo())
console.log(stu.getName())
// console.log(stu.age()) // 报错,protected其他类不能访问
7.静态属性 静态方法
class Person {
name: string;
static age: number;
constructor() {
this.name = 'za';
}
static getName() {
return this.name;
}
}
let p1 = new Person()
console.log(Person.age) //
// console.log(p1.age) // 报错 不是实例属性
Person.getName();
// p1.getName() // 报错 不是实例属性
8.装饰器
一种特殊类型的声明, 可以被附加到类声明,属性,方法,或参数上,可以修改类的行为
1.类装饰器
namespace a1 {
interface Person {
xx: string,
yy: string
}
function enhancer (target: any) {
target.prototype.xx = 'xx';
target.prototype.yy = 'yy';
}
@enhancer
class Person {
constructor() {}
}
let p = new Person();
console.log(p.xx) //
console.log(p.yy) //
}
namespace a2 {
// 把类整个替换
// interface Person {
// age: number
// }
// function enhancer (target: any) {
// return class {
// public name: string = 'person1'; // Person中有的,装饰器必须有
// public age: number = 10;
// }
// }
// @enhancer
// class Person {
// name: string = 'person';
// constructor() {}
// }
// let p = new Person();
// console.log(p.age) //
interface Person {
age: number
}
function enhancer (target: any) {
return class extends target{
public age: number = 10;
public name: string = 'dhy';
}
}
@enhancer
class Person {
name: string = 'person';
constructor() {}
}
let p = new Person();
console.log(p.age) //
// interface Person {
// age: number
// }
// function enhancer(name: string) {
// return function (target: any) {
// return class extends target{
// public name: string = name; // Person中有的,装饰器必须有
// public age: number = 10;
// }
// }
// }
// @enhancer('zs') // 参数
// class Person {
// name: string = 'person';
// constructor() {}
// }
// let p = new Person();
// console.log(p.age) //
// console.log(p.name) //
}
2.属性装饰器(方法装饰器)
// 属性装饰器 用来装饰属性
// 会在运行时当作函数被调用,传入两个参数
// 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
// 2.属性的名称
// 方法装饰器 用来装饰方法三个参数
// 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
// 2.方法的名称
// 3.方法描述符
namespace c1 {
// target如果装饰的是普通属性,target指向类的原型 Person.prototype
// target装饰的是类的属性static,那么target指向类的定义
function upperCase(target: any, propertyName: string) {
let value = target[propertyName];
const getter = () => value;
const setter = (newVal: string) => {
value = newVal.toUpperCase();
}
delete target[propertyName];
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
})
}
function propertyEnumerable(flag: boolean) {
return function(target: any, propertyName: string) {
Object.defineProperty(target, propertyName, {
enumerable: flag,
configurable: true,
})
}
}
function methodEnumerable(flag: boolean) {
return function(target: any, methodName: string,propertyDescriptor: PropertyDescriptor) {
propertyDescriptor.enumerable = flag;
}
}
function setAge(age: number) {
return function(target: any, methodName: string,propertyDescriptor: PropertyDescriptor) {
target.age = age; // target指向静态类Person
}
}
function toNumber(target: any, methodName: string,propertyDescriptor: PropertyDescriptor) {
console.log('methodName',methodName)
console.log('propertyDescriptor',propertyDescriptor)
let oldMethod = propertyDescriptor.value;
propertyDescriptor.value = function(...args: any[]) {
args = args.map(item => parseFloat(item));
return oldMethod.apply(this, args)
}
}
class Person {
static age:number;
@upperCase
@propertyEnumerable(false)
name: string = 'zs'
@methodEnumerable(true)
getName() {
console.log('getName')
}
@setAge(100)
static getAge() {}
@toNumber
sum(...args: any[]){
return args.reduce((accu, item) => accu + item, 0)
}
}
let p1 = new Person()
// p1.name = 'lll'
// console.log(p1.name)
// for (let attr in p1) {
// console.log('attr = ' + attr)
// }
// console.log(Person.age)
console.log(p1.sum(1, '2', '3'))
}
3.参数装饰器
namespace d1 {
// 三个参数
// 1.Person.prototype
// 2.login
// 3. 1 索引 userName 0 passWord 1
interface Person {
age: number
}
function addAge(target: any, methodName: string, paramsIndex: number){
target.age = 10
}
class Person {
login(userName: string,@addAge passWord: string) {
console.log(this.age, userName, passWord)
}
}
let p = new Person();
p.login('zs', '123');
}
4.装饰器执行顺序
1.属性和方法(属性和方法谁在前面谁先执行)
2.方法的时候,先参数在方法,他们一定会在一起
3.最后是类
4.如果是同类型的,先执行后写,类装饰1和类装饰2先执行2
9.抽象类 抽象方法
// 抽象描述一种抽象的概念,无法被实例化,只能被继承
// 无法创建抽象类的实例
// 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
abstract class Animal {
name: string;
abstract getName(): string
}
class Cat extends Animal {
getName(): string {
return this.name;
}
}
let cat = new Cat();
cat.name = 'mao';
console.log(cat.getName())
10.抽象类 vs 接口
// 接口
// 接口一方面可以表示为行为的抽象,也可以用来描述对象的形状(对象有哪些属性,属性是什么类型)
// 接口就是把一些类中共有的属性和方法抽象出来,可以用来约束实现此接口的类
// 一个类可以继承另一个类并实现多个接口
// 接口就像插件一样是用来增强类的,而抽象类是具体类的抽象概念
// 一个类可以实现多个接口,一个接口也可以被多个类实现,一个类可以有多个子类,但只有一个父类
//用来描述对象的形状(对象有哪些属性,属性是什么类型)
interface Point {
x: number,
y: number
}
let point: Point = {
x: 0, y: 0
}
// 可以表示为行为的抽象
interface Sppeakable {
speak(): void // 接口里不能放实现,只能放定义,所有的方法都是抽象的
}
interface Eatable {
eat(): void
}
// 一个类可以实现多个接口,一个接口也可以被多个类实现,一个类可以有多个子类,但只有一个父类
class Person implements Sppeakable, Eatable{
speak() {}
eat() {}
}
let p = new Person()
console.log(p.eat)
// 抽象类 vs 接口
// 不同类之间公有的属性和方法,可以抽象成一个接口
// 而抽象类是供其他类继承的基类,抽象类不允许被实例化,抽象类的抽象方法必须在子类中被实现
// 抽象类本质上是一个无法被实例化的类,其中能够实现方法和初始化属性,而接口仅能够用于描述,既不能提供方法的实现,也不能为属性初始化
// 一个类可以继承一个类或者抽象类,但可以实现多个接口
// 抽象类也可以实现接口
namespace a {
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
abstract speak(): void;
}
interface Flying {
fly(): void
}
class Duck extends Animal implements Flying {
speak() {
console.log('ddd')
}
fly() {
console.log('fff')
}
}
let d = new Duck('duck');
d.speak();
d.fly();
}
11.重写 override vs 重载 overload
// 重写 是指子类重写继承自父类的方法
// 重载是指同一个函数提供多个类型的定义
class Animal {
constructor() {
}
speak() {
console.log('动物叫')
}
}
class Cat extends Animal {
speak() {
console.log('m喵喵喵');
super.speak(); // 父类的speak方法调用方式
}
}
let c = new Cat();
c.speak()
12.继承 vs 多肽
- 继承 子类继承父类,子类除了拥有父类的所有特性外,还有些更具体的特性
- 多肽 由继承而产生了相关的不同类,对同一个方法可以有不同行为(重写)
七. 接口
1.任意的属性
interface PlanObject {
[PropName: string]: number
}
let obj: PlanObject = {
x: 1,
y: 3,
z: 2,
s: 2
}
2.接口的继承
interface Speakable {
speak(): void;
}
interface SpeakChinese extends Sppeakable {
speakChinese(): void;
}
class Person implements SpeakChinese {
speak(){}
speakChinese(){}
}
3.接口的readonly
interface Circle {
readonly PI: number;
radius: number
}
let circle: Circle = {
PI: 3.14,
radius: 10
}
// circle.PI = 3.15 // 报错 readonly不能修改
4.接口还可以用来约束函数
interface Discount {
(price: number): number // : number函数返回类型
}
let cost: Discount = function(price: number): number {
return price * .8;
}
5.可索引接口 用来对数组和对象进行约束
interface UserInterface {
[index: number]: string // 类似任意属性
}
let arr: UserInterface = [ '1','2','3']
let obj3: UserInterface = {
1: '1',
2: '2',
3: '3'
}
6.类接口 可以用接口来装饰类 (ts两个核心:接口+泛型)
iinterface Speakable {
name: string;
speak(word: string): void
}
class Dog implements Sppeakable {
name: string;
speak() {}
}
7.约束构造类型 使用new来约束
class Animal {
constructor(public name: string) {
}
}
interface WidthNameClass {
new(name: string): Animal
}
function createAnimal(clazz: WidthNameClass, name: string) {
return new clazz(name)
}
let a = createAnimal(Animal, 'miaomiao')
八.泛型
泛型 是指在定义函数,接口或类的时候,不预先指定具体的类型,在使用的时候再指定类型的一种特性
泛型 T 作用域只限于函数内部使用
1.初体验
function createArray(length: number, value: any): Array<any> {
let result: Array<any> = [];
for(let i = 0;i < length; i++) {
result[i] = value;
}
return result;
}
let result = createArray(3, 'x');
console.log(result)
function createArray1<T>(length: number, value: T): Array<T> {
let result: Array<T> = [];
for(let i = 0;i < length; i++) {
result[i] = value; // 报错,确定类型为string
// result[i] = 1; // 报错,确定类型为string
}
return result;
}
let result1 = createArray1<string>(3, 'x');
let result2 = createArray1<number>(3, 2);
2.类数组 ArrayLike arguments
function sum(...args2: any[]) {
let args: IArguments = arguments; // IArguments,HTMLElement ts自带
for(let i = 0; i< args.length; i++) {
console.log(args[i])
}
}
sum(1,2,3)
let root: HTMLElement | null = document.getElementById('root');
let children: HTMLCollection = root.children;
let childNodes: NodeListOf<ChildNode> = root.childNodes
3.类泛
private List: T[] = [];
add(val: T) {
this.List.push(val)
}
getMax(): T {
let result: T = this.List[0];
this.List.forEach(item => {
if(item> result) {
result = item
}
})
return result;
}
}
let arr = new MyArray<number>();
arr.add(1)
arr.add(5)
arr.add(7)
console.log(arr.getMax())
4.接口泛型
interface Calculate {
<T>(a: T, b: T): T
}
let add: Calculate = function <T>(a: T,b: T): T {
return a;
}
console.log(add<number>(5,3))
interface Cart<T> {
list: T[]
}
let cart: Cart<string> = {
list: ['1','2','3']
}
5.多个类型参数
// 如何在不增加中间变量的情况下,交换两个变量的值
function swap<A, B>(tuple:[A, B]): [B, A] {
return [tuple[1], tuple[0]];
}
console.log(swap<string, number>(['zs',18]))
6.默认泛型类型
function createArray2<T = number>(length: number, value: T): Array<T> {
let result: Array<T> = [];
for(let i = 0;i < length; i++) {
result[i] = value;
}
return result;
}
let result2 = createArray2(3, '2');
7.泛型的约束
// 在函数中使用泛型的时候,由于预先并不知道具体的类型,所以不能访问相应类型的方法
interface LengthWise {
length: number
}
function logger<T extends LengthWise>(val: T) {
console.log(val.length) //不能用lenth方法,还不知道val类型
}
8.泛型类型别名
type Cart2<T> = {list: T[]} | T[]
let c1:Cart2<string> = {list: ['1']}
let c2:Cart2<number> = [1]
九.结构类型系统
1.接口的兼容性
// 如果传入的变量和声明的类型不匹配,TS就会进行兼容性检查
// 原理是 Duck-Check,就是说只要目标类型中声明的属性变量在源类型中都存在就是兼容的
// ts跟类型没有关系,只跟属性有关系
interface Animal {
name: string;
age: number;
}
interface Person {
name: string;
age: number;
speak: (work: string) => void
}
function getName(animal: Animal): string {
return animal.name
}
let p : Person = {
name: 'zs',
age: 10,
speak() {}
}
console.log(getName(p))
2.基本类型的兼容性
let num: string | number
let str: string = 'nna'
num = str;
// num = 1;
str = num;
let num2: {
toString(): string
}
let str2: string = 'ddd'
num2 = str2 // 只要有toString都可以赋值
3.类的兼容性
// 只和属性有关
class Animal {
name: string
}
class Bird extends Animal {
swing: number
}
let a: Animal;
a = new Bird(); // 父类的变量能指向子类的实例
a = {name: 'zsd'} // 不管这个对象是我具体类型,只要属性有就可以
let b: Bird;
// b = new Animal() // 报错 子类的变量不能指向父类的实例
4.函数的兼容性
1)比较参数
type sumFunction = (a:number, b: number) => number;
let sum: sumFunction;
function f1(a:number, b: number): number {
return a;
}
sum = f1;
function f2(a:number): number {
return a;
}
sum = f2;
function f3(): number {
return 1;
}
sum = f3;
function f4(a:number, b:number, c: number): number {
return a;
}
sum = f4; // 报错 参数可以少不可以多
2)比较返回值
type GetPerson = () => { name: string, age: number }
let getPerson: GetPerson;
function g1() {
return { name: 'zs',age: 10}
}
getPerson = g1
function g2() {
return { name: 'zs'}
}
// getPerson = g2 // 报错 返回值只能多不能少
function g3() {
return { name: 'zs',age: 10, home: 'beijing'}
}
getPerson = g3
5.函数参数的协变
type logFunc = (a: number | string) => void;
let log: logFunc;
function log1(a: number | string | boolean) {
console.log(a)
}
log = log1
log1(true)
log(true) // 报错
function log2(a: number) {
console.log(a)
}
// log = log2 // 报错
}
6.泛型的兼容性
// 泛型在判断兼容性的时候会先判断具体的类型,然后在进行兼容性性判断
// interface Empty<T> {
// }
// let x: Empty<string>;
// let y: Empty<number>;
// x = y; // ok 因为Empty里面没有东西都是空对象 先判断具体的类型,然后在进行兼容性性判断
interface Empty<T> {
data: T
}
let x: Empty<string>;
let y: Empty<number>;
// x = y; // 报错
7.枚举的兼容性
// 枚举与数字类型兼容,并且数字类型与枚举类型兼容
// 不同枚举类型之间是不兼容的
enum Colors {
Red, Yellow
}
let c1: Colors;
c1 = Colors.Red; // 0
c1 = 1;
let d1: number;
d1 = Colors.Yellow; // 1
十.类型保护
类型保护 就是更精确的知道哪种类型
类型保护就是一些表达式,他们在编译的时候就能通过类型信息确保某个作用域内变量的类型
类型保护就是能够通过关键字判断出分支中的类型
1.typeof 类型保护
function double(input: string | number | boolean) {
// input.toLowerCase(); // 报错
if(typeof input === 'string') {
input.toLowerCase();
} else if (typeof input === 'number') {
input.toFixed(2);
} else {
input
}
}
2.instanceof类型保护
class Animal {
public name: string = 'zs';
}
class Bird extends Animal{
public swing: number = 2;
}
function getName(a: Animal) {
if(a instanceof Bird) {
a.swing;
} else {
a.name
}
}
3.null保护
function getFistLetter(s: string | null) {
s = s || '';
return s.charAt(0)
}
4.链判断运算符
// 链判断运算符是一种先检查属性是否存在,再尝试访问该属性的运算符,其符号为?.
// 如果运算符左侧的操作符?.计算为undefined或者null,则表达式求值为undefined。否则,正常触发目标属性访问,方法或者函数调用
a?.b;// 如果a是null/undefined,那么返回undefined,否则返回a.b的值
a === null?undefined:a.b;
5.可辨识的联合类型
// 就是利用联合类型中的共有字段进行类型保护的一种技巧
// 相同字段的不同取值就是可辨识
interface WarningButton {
class: 'waring',
text1: '修改'
}
interface DangerButton {
class: 'danger',
text2: '删除'
}
type Button = WarningButton | DangerButton;
function getButton(button: Button) {
if(button.class === 'waring') {
button.text1
} else {
button.text2
}
}
6.in操作符
interface Bird{
swing: number
}
interface Dog{
leg: number
}
function getNumber(x: Bird | Dog) {
if('swing' in x ) {
x.swing;
} else {
x.leg;
}
}
7.自定义的类型保护
interface Bird{
legs: number,
name1: 'bird'
}
interface Dog{
legs: number,
name2: 'dog'
}
function isBird(x: Bird | Dog): x is Bird {
return x.legs === 2;
}
function getAnimal(x: Bird | Dog) {
if(isBird(x)) {
x.name1; // bird
} else {
x.name2;
}
}
let x: Bird = {
name1: 'bird',
legs: 2
}
getAnimal(x);
十一.类型变换
1.交叉类型 就是二个接口类型的属性的并集
interface Bird{
name: string;
fly():void
}
interface Person{
name: string;
eat():void
}
type BirdMan = Bird & Person;
let p: BirdMan = {
name: 'zs',
fly() {},
eat() {}
}
2.typeof 以获取一个变量的类型
let p = {
name: 'zs',
age: 19
}
type Person = typeof p;
let p2:Person = {
name: 'ls',
age: 10
}
3.索引访问操作符 通过[]获取一个类型的子类型
interface Person2 {
name: string,
age: number,
job: {
name: string
},
// interests: { name: string, level: number}[]
interests: Array<{ name: string, level: number}>
}
let myjob: Person2['job'] = {
name: 'zs'
}
let myname: Person2['job']['name'] = 'zs'
let mylevel: Person2['interests'][0]['level'] = 10
4.keyof 索引类型查询操作符
interface Person3 {
name: string,
age: number,
genter: 'male' | 'female'
// [propName: string]: any
}
// function getValueByKey(val: Person3, key: string): any {
// return val[key] // 报错 key不一定在val中 两种方案 1,interface加propName(不用)
// }
// type Person3Key = 'name' | 'age' | 'genter'
type Person3Key = keyof Person3
function getValueByKey(val: Person3, key: Person3Key): any {
return val[key]
}
let person3: Person3 = {
name: 'zs',
age: 10,
genter: 'male'
}
let name = getValueByKey(person3, 'name')
// let name1 = getValueByKey(person3, 'name1') // 报错
5.映射类型 在定义的时候用in操作符去批量定义
interface Person4{
name: string,
age: number,
genter: 'male' | 'female'
}
type PartialPerson = {
[key in keyof Person4]?: Person4[key]
}
// type PartialPerson = {
// name?: string,
// age?: number,
// genter?: 'male' | 'female'
// }
// let p4: Person4 = { //报错 不能少属性
// name: 'zs',
// age: 10,
// }
let p4: PartialPerson = {
name: 'zs',
age: 10,
}
6.内置工具类型 ts中内置了一些工具类型来帮我们更好的使用类型系统
1.partial 可以将传入的属性由非可选变为可选
interface Person4{
name: string,
age: number,
genter: 'male' | 'female'
}
// type Partial<T> = {
// [key in keyof T]?: T[key]
// }
type PartialPerson = Partial<Person4>
let p4: PartialPerson = {
name: 'zs',
age: 10,
}
2.required 可以将传入的属性中的可选项变为必选项,这里用了-?修饰符来实现
interface Person5{
name?: string,
age?: number,
genter?: 'male' | 'female'
}
// type Required<T> = {
// [key in keyof T]-?: T[key]
// }
type RequiredPerson = Required<Person5>
let p5: RequiredPerson = {
name: 'zs',
age: 10,
genter: 'male'
}
3.readonly 可以将传入的属性中的项变为只读
interface Person6{
name: string,
age: number,
genter: 'male' | 'female'
}
// type Readonly<T> = {
// readonly [key in keyof T]: T[key]
// }
type ReadonlyPerson = Readonly<Person6>
let p6: ReadonlyPerson = {
name: 'zs',
age: 10,
genter: 'male'
}
// p6.name = 'sff' // 报错,只读不能赋值
4.pick
interface Person7{
name: string,
age: number,
genter: 'male' | 'female'
}
type Pick<T, K extends keyof T> = {
[key in K]: T[key]
}
type PickPerson = Pick<Person7,'name'>
let p7: PickPerson = {
name: 'zs',
// age: 10,
// genter: 'male'
}
5.映射类型修饰符的控制
// -? 去除可选
// +? 添加可选
//
// TS 类型和值
// 类型 interface class enum type
// 值 let var const function class enum
let x = {
name: 'zs'
}
let a = typeof x; // let a: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
type b = typeof x;
// type b = {
// name: string;
// }
// interface type class
// interface是定义接口的类型,他是真实的类型,可以被导出和倒入
// type 只是临时的别名,并不会产出真正的类型
// class 定义类 new xxx
7.条件类型
1.定义条件类型
interface Fish {
name1: string
}
interface Fish2 {
name1: string,
age: number
}
interface Water {
name2: string
}
interface Bird {
name3: string
}
interface Sky {
name4: string
}
type Condition<T> = T extends Fish ? Water : Sky
let condition: Condition<Fish> = {
name2: 'water'
}
// extends Fish2里的属性Fish中有就是继承
let condition1: Condition<Fish2> = {
name2: 'water'
}
2.条件类型的分发
interface Fish {
name1: string
}
interface Water {
name2: string
}
interface Bird {
name3: string
}
interface Sky {
name4: string
}
type Condition2<T> = T extends Fish ? Water : Sky
// Condition2<Fish | Bird> = Water | Sky
let c1: Condition2<Fish | Bird> = {name2: 'water'}
let c2: Condition2<Fish | Bird> = {name4: 'water'}
let c3: Water | Sky = {name4: 'water'}
let c4: Water | Sky = {name4: 'water'}
3.内置条件类型
1.Exclude 从T可分配给的类型中排除U
type E = Exclude<string | number, number>
// let e: E = 10; // 报错
let e: E = '10';
2.Extract 从T可分配给的类型中提取U
type E1 = Extract<string | number, number>
// let e1: E1 = '10'; // 报错
let e1: E1 = 10;
3. NonNullable 从T中排除null和undefined
type E2 = NonNullable<string | number | null | undefined>
let e21: E2 = '10';
let e22: E2 = 10;
4.ReturnType 获取函数的返回值的类型 redux
function getUserInfo() {
return { name: 'zs',age: 10}
}
type UserInfo = ReturnType<typeof getUserInfo>;
let user: UserInfo = {name: 'zs',age: 20}
5.instanceType 获取构造函数的实例类型
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
type P = InstanceType<typeof Person>;
let p:P = new Person('zs')
十二.模块vs命名空间
1.模块
export.ts
export let a =10;
export let b = 10;
export default 30;
import.ts
import { a,b} from './export'
2.命名空间
// 1.封装类似的代码
// 2.防止命名冲突
namespace zoo {
class Dog {
}
}
namespace home {
export class Dog {
}
}
let dog = new home.Dog()
let dog2 = new zoo.Dog() // 不能用,没有导出
十三.类型声明
声明文件可以让我们不需要将JS重构为TS,只需要加上声明文件就可以使用系统
类型声明在编译的时候会被删除,不会影响真正的代码
1.普通类型声明
declare const $: (selector string) => {
click(): void
width(length: number): void
}
declare let name: string;
declare let age: number;
declare function getName():string;
declare class Animal { name: string }
interface Person {
name: string
}
type Student = Person | string;
2.外部枚举
declare const enum Seasons {
Spring,
Summer,
Autumn,
Winter
}
let seasons: Seasons[] = [
Seasons.Spring,
Seasons.Summer,
Seasons.Autumn,
Seasons.Winter
]
console.log(seasons)
3.namespace
declare namespace jQuery {
function ajax(url: string, config: any): void;
let name: string;
namespace fn {
function extend(object: any): void
}
}
jQuery.ajax('/api/users',{});
jQuery.name;
jQuery.fn.extend({})
4.类型声明文件
我们可以把类型声明放在一个单独的类型声明文件中
可以在类型声明文件中使用类型声明
文件命名规范为'.d.ts'
观看类型文件有助于了解库的使用方式
1.jquery.d.ts
declare const $:(selector: string) => {
click(): void
width(length: number): void
}
2.tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"outDir": "./",
}
"include": [
"src/**/**",
"typings?**/**"
]
}
3.test.js
$('#root').click();
$('#root').width(10)
5.第三方声明文件
可以安装使用第三方的声明文件
@types是一个约定的前缀,所有的第三方声明的类型库都会带有这样的前缀
javascript 中有很多内置对象,他们可以被当成声明好的类型
内置对象是指根据标准在全局作用域上存在的对象,这里的标准是指ECMAScript和其他环境(比如DOM)的标准
这些内置对象的类型声明文件,就包含在TypeScript核心库的类型声明文件中
1.使用jquery
// 对于common.js风格的模块必须使用 import * as
import * as jQuery from 'jquery';
jQuery.ajax('/user/1')
2.安装声明文件
cnpm i @types/jquery -S
3.自己编写声明文件
// types/jquery/index.ts
declare function jQuery(selector:string):HTMLElement;
declare namespace jQuery{
function ajax(url:string):void
}
export default jQuery;
tsconfig.json
// 如果配置了path,那么在引入包的时候会自动去paths目录里找类型声明文件
// 在webpack中,我们可以通过配置alias的形式来为项目的文件做映射。在tsconfig.json中,我们同样也可以做映射
// 在tsconfig.json中,我们通过complierOptions里的paths属性来配置路径映射
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {
"*": [
"types/*"
]
},
4.npm声明文件可能的位置
node_modules/jquery/package.json
"types":"types/xxx.d.ts"
node_modules/jquery/index.d.ts
node_modules/@types/jquery/index.d.ts