环境搭建
执行npm install -g typescript命令之后,全局会增加一个tsc命令
执行tsc 1.ts就可以将1.ts转换为1.js
每次都通过命令执行较为繁琐,可以在vscode中安装code runner,右击run code执行,在执行ts脚本的过程中,会提示没有ts-node这个命令,可以通过npm install ts-node -g全局安装一下
tsc --watch也可以监听文件变化
非空断言操作符:
案例1:赋值时使用
function simpleExample(a: number | undefined) {
// ts认为,a有可能是number,也可能是undefined,用了a!之后,就告诉ts a一定不是undefined
const b: number = a; // COMPILATION ERROR: undefined is not assignable to number.
const c: number = a!; // OK
}
案例2:调用方法时使用
let strOrNum: string | number;
// ! 非空断言,我断定这个变量 一定有值. 出错了 自负责
strOrNum!.toString()
案例3:非空断言和as一起使用
let strOrNum: string | number;
(strOrNum! as string).toLowerCase()
在非strictNullChecks值为false的模式下,undefined null是任意类型的子类型
双重断言:
let name4:string|number;
//这种写法可以避免编译器报错
console.log(name4! as any as boolean);
断言不推荐写法(容易和泛型混淆)
let strOrNum: string | number;
(<number>strOrNum!).toFixed()
字面量类型
const username: "jw" = "jw";
const password: 123456 = 123456;
常用到的场景:
// type关键字 和 enum 都是ts提供的 和js没关系
type Direction = "up" | "down" | "left" | "right";
let direction: Direction = "down"; // 字面量类型 就是限定了值 和 枚举类似
type和interface的区别
type用来定义一个类型或者用来做类型别名用,相当于存储类型的一个变量,而interface是接口 interface可以定义多个同名的接口,但type不可以 interface可以extends继承或者implements实现,但type不可以,type需要用&连接两个类型以达到类似目的
// 对象可以有任意属性
interface Person2 {
[key:string]: any;
}
组合优于继承,因此能用接口就不用type
函数类型接口
interface Discount {
(price: number): number
}
const discount: Discount = (price: number): number=>{
return price*.8;
}
可索引接口
//对数组和对象进行约束
interface User{
[xx:number]:string
}
let user: User={
0:'0',1:'1',2:'2'
}
let arr: User=['1','2','3'];
构造函数类型
class Animal{
constructor(public name:string){
}
}
//如果是修饰普通函数
//加上new之后就是用来描述类的构造函数
interface WithNameClass{
new(name:string):any
}
let wc: WithNameClass = Animal
function createClass(clazz: WithNameClass,name:string){
return new clazz(name);
}
泛型接口
第1种:在方法的定义阶段就需要确定类型
interface Calculate<T>{
(a:T,b:T):T
}
let sum: Calculate<number> = function (a: number, b: number): number {
return a + b;
};
sum(1,2);
第2种:在方法的调用阶段确定类型
interface Calculate2{
<T>(a:T,b:T):T
}
let sum2: Calculate2 = function <T>(a: T, b: T): T {
return a;
};
sum2<number>(1, 2);
第3种
interface Calculate3<T>{
<U>(a:T,b:T):U
}
let sum3: Calculate3<number> = function <U>(a: number, b: number): U {
return a as any;
};
sum3<string>(1, 2);
泛型约束
interface LengthWise{
length:number
}
//非常非常非常重要
function logger2<T extends LengthWise>(val: T) {
console.log(val.length);
}
logger2<string>(‘abc’);
let obj = {
length:10
}
type Obj = typeof obj;
logger2<Obj>(obj);
函数兼容性
type Func = (a:number,b:number)=>void;
let sum: Func;
function f1(a: number, b: number):void {
}
sum = f1;
//参数少一个可以
function f2(a: number): void {}
sum = f2;
//少二个参数也可以
function f3(): void {}
sum = f3;
function f4(a: number, b: number,c:number): void {}
//sum = f4;
type GetPerson = ()=>{name:string,age:number}
let getPerson: GetPerson;
function g1(){
return {name:'zhufeng',age:10};
}
getPerson=g1;
function g2() {
return { name: "zhufeng", age: 10,gender:0 };
}
getPerson = g2;
function g3() {
return { name: "zhufeng" };
}
// g3不能赋值给getPerson,因为g3调用后可能去取age属性,但g3明显没返回age属性,因此会有问题
//getPerson = g3;
==================================
class Animal {}
class Dog extends Animal {
public name: string = "Dog";
}
class BlackDog extends Dog {
public age: number = 10;
}
class WhiteDog extends Dog {
public home: string = "北京";
}
type Callback = (dog: Dog) => Dog;
function exec(callback: Callback): void {}
type ChildToChild = (blackDog: BlackDog) => BlackDog;
let childToChild: ChildToChild;
exec(childToChild);//n
// Argument of type 'ChildToChild' is not assignable to parameter of type 'Callback'.
// Types of parameters 'blackDog' and 'dog' are incompatible.
// Property 'age' is missing in type 'Dog' but required in type 'BlackDog'.ts(2345)
// 18.ts(9, 10): 'age' is declared here.
type ChildToParent = (blackDog: BlackDog) => Animal;
let childToParent: ChildToParent;
exec(childToParent);//n
type ParentToParent = (animal: Animal) => Animal;
let parentToParent: ParentToParent;
exec(parentToParent);//n
// Argument of type 'ParentToParent' is not assignable to parameter of type 'Callback'.
// Property 'name' is missing in type 'Animal' but required in type 'Dog'.ts(2345)
// 18.ts(6, 10): 'name' is declared here.
type ParentToChild = (animal: Animal) => BlackDog;
let parentToChild: ParentToChild;
exec(parentToChild);//y
原则:为了在使用的时候能够访问到对应的成员
既然要使用一个东西,首先就需要找到这个东西的提供方,即明确提供方和使用方
提供方提供了我使用时需要的,那就OK,否则就不可以
以上述案例exec(childToChild);为例:
对于childToChild这个回调来说,它里面可能会用到blackDog这个参数,这个参数需要用到name age两个属性,因此这个回调里的函数体就是使用方
谁给childToChild这个函数体提供这个参数呢?自然是调用childToChild的地方,谁来调用呢?exec的函数体中会调用它,因此,exec的函数体就是提供方,那exec提供方又提供了什么对象呢?需要到exec的形参中去找,exec的形参是一个会返回Dog的对象,它提供的属性只有name
因此提供了name,但需要用到name age,因此校验不通过
而对于exec这个方法来说,它里面会用到调用childToChild返回的对象,因此这次提供方变成childToChild的返回值,而使用方变成了exec的方法体
提供方提供的返回值是BlackDog类型,是一个拥有name age的对象,而使用方exec需要的是Dog类型,它只需要name属性即可,因此校验通过
==========================
type S = (a: number, b: string) => void
type T = (x: number) => void
let fn: S = (x: number, b: string) => {}
let func: T = fn
// Type 'S' is not assignable to type 'T'.
// 类型 S 不能赋值给类型 T,因为参数个数不兼容
==========================
type T = (a: number, b: string) => void
type S = (x: number) => void
let fn: S = (x: number) => {}
let func: T = fn
类型谓词
y is Bird整体是一个类型
function isBird(y:Bird|Dog):y is Bird{
return (y as Bird).swing == 2;
}
function getAnimal(x: Bird | Dog) {
if(isBird(x)){
console.log(x);
}else{
console.log(x);
}
}
unknown
unknown,是any的安全类型
交叉类型
理解时需要按照子类型理解,而不能按照并集和交集的思路去想
interface A{name:string,c:number}
interface B{age:number,c:number}
let a:A;
let b:B;
type C = A&B;
let c:C = {name:'zhufeng',age:10,c:10};
// 原则:A和B交叉以后的类型可以赋值给A或B
a=c;
b=c;
// 从限制范围的角度也可以理解:当给一个对象加的属性越来越多的时候,表示的范围会越来越窄,因此c虽然看起来属性多,但是反而是限制更死了,限制了A&B 必须是满足接口A的所有对象中包含age属性的那部分对象,或者是满足接口B中的所有对象中包含name属性的那部分对象
type AA = string|number;
type BB = string|boolean;
// CC只有是string时才可以既赋值给AA,也可以赋值给BB
type CC = AA&BB;
// 实例
function mixin<T,U>(one:T,two:U){
const result = <(T & U)>{};
for(let key in one)
(<T>result)[key] = one[key];
for (let key in two)
(<U>result)[key] = two[key];
return result;
}
const x = mixin({name:'zhufeng'},{age:11});
console.log(x.name,x.age);
==========================
Partial类型:
type Partial<T> = {
[key in keyof T]?: T[key]
}
type PPerson = Partial<Person6>
==========================
条件类型:
interface Fish{
name1:string;
}
interface Water{
name2:string;
}
interface Bird{
name3:string;
}
interface Sky{
name4:string;
}
//type Condition<T> = T extends Fish?Water:Sky;
type Condition<T> = { t: T } extends { t: Fish} ? Water : Sky;
//let con:Condition<Fish> = {name2:'水'};
//条件类型的分发
let con1: Condition<Fish | Bird> = { name4:''};
let con2: Condition<Fish | Bird> = { name4: '' };
// 找出T中不包含U的部分
type Diff<T,U> = T extends U?never:T;
type R = Diff<'a'|'b'|'c'|'d' , 'a'|'b'|'c'>;
type R2 = 'd';
type Filter<T,U> = T extends U?T:never;
type R3 = Filter<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>;
==========================
内置条件类型
//Exclude
type Exclude<T, U> = T extends U ? never : T;
type R4 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>;
type Extract<T, U> = T extends U ? T : never;
//Extract
type R5 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'b' | 'c'>; //Pick
//NonNullable
type NonNullable<T> = T extends null | undefined ? never : T;
type R6 = NonNullable<'a' | null | undefined>;
//ReturnType
// infer R相当于定义了一个变量,这个语法用来获取一个表达式中某部分对应的类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T;
(...args: any[]) => infer R 是一个表达式
T extends (...args: any[]) => infer R 是一个表达式
function getUser(a:string,b:number){
return {name:'zhufeng',age:10};
}
//let t = getUser();
type GetUserType = typeof getUser;
type ReturnUser = ReturnType<GetUserType>;
let u: ReturnUser = {
name:'zf',
age:10
}