前言
typescript作为javascirpt超集,主要在开发中基础也是最重要的就是约定类型增强代码安全性和可读性,至于类型算法的方面在本文暂时不过多展开
基础类型
1.string
let name:string = 'bob';
name = 'smith';
2.number
let dec:number = 6;
let hex:number = 0Xf00d;
let oct:number = 0o744;
let bin:number = 0b1010;
var dec = 6;
var hex = Oxf00d;
var oct = 484;
var bin = 10;
3.boolean
let isDone:boolean = false;
4.array
定义非联合类型数组
使用泛型定义类型:
let arr:Array<number> = [1,2,3];
使用[]定义:
let arr:number[] = [1,2,3];
定义联合类型数组
使用泛型定义类型:
let arr1:Array<number | string> = [1,'2',3];
使用[]定义:
let arr:(number | string)[] = [1,2,'4'];
元组:
const arr4:[string,number,boolean] = ['1',2,true];
const arr6:[string,number?,boolean?] = ['linbudu'];
const arr6:[string,number?,boolean?] = ['linbudu',,]
具名元祖:
const arr7:[name1:string,age:number,male:boolean] = ['linbudu',599,true];
const [name1,age,male] = arr7;
console.log(name1,age,male);
var arr7 = [
'linbudu',
599,
true,
]
var name1 = arr7[0],age = arr7[1],male = arr7[2];
5.undefined
let u:undefined = undefined;
6.null
let n:null = null;
7.any
8.unknown
9.void
function abc():void{
console.log(1);
}
10.never
function err(msg: string): never {
throw new Error(msg);
}
function loopForever(): never {
while (true) {};
}
11.symbol
const symbolVar:symbol = Symbol('unique');
接口(interface)
interface 变量名{
属性名:数据类型,
1.传入的参数名必须是定义的属性名
2.传入的参数数据类型必须是规定的数据类型
3.传入的参数个数必须和接口中定义的数量保持一致
属性名?:数据类型,
1.传入的参数个数可有可无
2.可以对预定义的传参,进行预定义
readonly 属性名:数据类型,
1.传参的属性名和接口的属性名相同时,会被设置为可读
2.属性为可读时,不能进行修改
}
1.定义
interface SearchFunc{
(source:string,subStrng:string):boolean;
}
interface SearchFunc{
(形参名1:数据类型1,形参名2:数据类型2):函数返回值类型
}
let mySearch:SearchFunc;
mySearch = function(src:string,substring:string){
let result = src.search(substring);
return result > -1;
}
1.定义:
interface StringArray{
[index:number]:string;
}
2.使用:
let myArray:StringArray;
myArray = ["Bob","Fred"];
let myStr:string = myArray[0];
interface Iperson{
name:string;
age:number;
setTime(d: number):number;
}
class Person implements Iperson{
name:string;
age:number;
setTime(d: number){
console.log(d);
return d;
};
constructor(h:string,m:number){
this.name = h;
this.age = m;
}
}
}
const obj:Iperson = {
name:'guang',
age:18,
setTime(a){
console.log(a);
return a;
}
}
interface Iperson{
name:string,
age:number,
}
interface PersonConstructor{
new(name:string,age:number):Iperson;
}
function createPeson(ctor:PersonConstructor):Iperson{
return new ctor('guang',18);
}
class Rperson implements Iperson{
name:string;
age:number;
constructor(n:string,a:number){
this.name = n;
this.age = a;
}
}
createPeson(Rperson);
interface User {
name: string
age: number
}
interface User {
sex: string
}
interface inherit {
mutation:string
}
interface pepole extends inherit {
attitude:string;
}
User 接口为 {
name: string
age: number
sex: string
}
type TStringArray = {
[index: number]: string;
}
type TStringArray = {
[index: string]: string;
}
type
情况1.
type Num = number;
type Type = number | string;
type Arr = [string,number];
let abc = 1;
type Types = typeof abc;
let def:Types = abc;
情况2.
type Person = {
name: string;
age: number;
};
type Employee = {
sex: string;
};
type EmployeePerson = Person & Employee;
let Nav:EmployeePerson = {
name:'name',
age:18,
sex:"12'"
}
情况4.
type User = {
name: string
age: number
};
情况5.
type SetUser = (name: string, age: number)=> void;
情况6.
type Call<T> = (data: T) => void;
继承
情况1.
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(input:Date) {
this.currentTime = input;
}
}
情况2.
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let obj123:Square = {
color:'color',
sideLength:12,
}
情况3.
class type {
name:string;
age:number;
constructor(a:string,b:number){
this.name = a;
this.age = b;
}
}
interface Port extends type{
name:string;
age:number;
}
情况4.
class Person {
name:string;
age:number;
constructor(name:string, age:number) {
this.name = name
this.age = age
}
running() {
console.log('running!!!')
}
}
class Student extends Person {
sno:number
constructor(name:string, age:number, sno:number) {
super(name, age)
this.sno = sno
}
studying() {
console.log('studying!!!')
}
}
const s1 = new Student('ice', 22, 200010)
s1.running()
s1.studying()
枚举(enum)
//枚举可以定义一些带名字的常量
//可以创建一组有区别的用例
情况1.数字枚举,当有初始值时,后面的元素会按位置顺序以1递增
enum Color{
Red = 1,
Green,
Blue,
}
console.log(Color.Red)
console.log(Color.Green)
console.log(Color.Blue)
情况2.数字枚举,当没有初始值时,起始值默认为0
enum Color{
Red,
Green,
Blue
}
console.log(Red)
console.log(Green)
console.log(Blue)
情况3.数字枚举,当没有初始值时,第一个成员的键值默认为0
enum Color{
Red,
Green = 0,
Blue,
}
console.log(Red)
console.log(Green)
console.log(Blue)
情况4.数字枚举,反映射(根据值进行反向获取枚举中的元素名)
enum Data{
web = 1,
tru = 2,
tree = 3,
}
let a:string = Data[1]
console.log(a)
情况5.字符串枚举,若初始化中有字符串时,需要对全部元素初始化
enum Data{
web = '1',
tru = '2',
tree = '3',
}
情况6.常量枚举
const enum Direction{
NORTH,
SOUTH,
EAST,
WEST
}
let dir:Direction = Direction.NORTH
情况7.枚举表达式
1.一个枚举表达式字面量(主要是字符串字面量或数字字面量)
2.一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
带括号的常量枚举表达式
3.一元运算符 +, -, ~其中之一应用在了常量枚举表达式
4.常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。
enum Demo{
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = "123".length
}
类型运算
1.条件类型基础:extends ?
interface A{
a:string
}
interface B{
b:string
}
type c = B extends A ? number : string
2.in可以遍历枚举类型,类似for...in
type Example = 'a' | 'b' | 'c' | 'd'
type Obj = {
[T in Example]:string
}
//type Obj = {a:string
3.keyof作为索引类型查询操作符,就是取到类型的key
interface Example{
a:string
b:string
c:number
d:boolean
}
type Keys = keyof Example
4.索引访问接口属性:
例1:
type person = {
name:"Angus"
height:85
}['name'|"height"]
//type person = 'Angus' | 85
例2:
type Clothes<source,types> = {
[K in keyof source]:source[K] extends types ? K : never
}[keyof source]
interface Example{
name:string
height:number
home:string
}
type newType = Clothes<Example,string>
例3:
interface Angus{
name:string
height:number
wight:number
}
type newAngus = Pick <Angus,'number' | 'height'>//{name:string
type Pick<T,K extends keyof T> = {
[P in K] : T[P]
}
例4:
type FileterType<Source,Types> = Pick<Source,
{
[K in keyof Source]:Souce[k] extends Types ? K : never
}[keyof Source]>
5.类型约束:
变量类型1 extends 类型2
6.infer推导:
infer R 代表待推断的返回值类型,方便后续作为变量使用
例1:解构1
type First<Tuple extends unknown[]> = Tuple extends [infer T,...infer R] ? T : never
type res = First<[1,2,3]>
//res = 1
例2:解构2
type Unpacked<T> = T extends (infer R)[] ? R : T
//如果T是某个待推断类型的数组,则返回推断的类型,否则返回T
type Ids = number[]
type Names = string[]
type idType = Unpacked<Ids>
type nameType = Unpacked<Names>
例3:使用约束条件获取对应类型
type Response = Promise<number[]>
type Unpacked<T> = T extends Promise<infer R> ? R : T
type resType = Unpacked<Response>
例4:同一个类型变量在推断的值有多种情况的时候会推断为联合类型
type Foo<T> = T extends {a:infer U
type T10 = Foo<{a:string
type T11 = Foo<{a:string
例5:
type ElementOf<T> = T extends (infer R)[] ? R : never
type TTuple = [string,number]
type Union = ElementOf<TTuple>
例6:传参不知道传什么参数时,用泛型+infer获取对应类型
type ref = React.MutableRefObject<G2plotStackedBar | undefined>
const chartRef = useRef<???>()
改进:
type ChartRef<T> = T extends React.MutableRefObjet<infer P> ? P : never
const chartRef = useRef<ChartRef<ref>>()
例7:以上的结合,实现ref
ref函数就是把这个值包裹成{value:T}这样的结构
1.一般情况
type Ref<T=any>={
value:T
}
function ref<T>(value:T):Ref<T>
const count = ref(2)
count.value://number
2.复杂情况
function ref<T>(value:T):T extends Ref ? T : Ref<UnwrapRef<T>>
//若情况为ref(ref(2)),内存的ref(2)返回的是Ref<number>,直接返回Ref<number>
//若情况为ref(ref({a:1})),返回的是Ref<{a:number}>,则直接返回Ref<{a:numebr}>
//若情况为ref(2),返回的是Ref<UnwrapRef<2>>,返回number类型
3.UnwrapRef实现(解决ref嵌套问题):
type UnwrapRef<T> = {
ref:T extends Ref<infer R> ? R : T,
other:T,
}[T extends Ref ? 'ref' : 'other']
4.进一步升级UnwrapRef
type UnwrapRef<T> = {
ref: T extnds Ref<infer R> ? R : T,
object:{[K in keyof T]:UnwrapRef<[T[K]>},
other:T
}[T extends Ref ? 'ref' : T extends object ? 'object' : 'other']
//若情况为ref({foo:ref(1),bar:ref(2)}),T的类型为object
//对对象中的每一项都进行,分别为{foo:number}、{bar:number}
typescript中常用的工具类型:
1.Omit:Omit(a,b)接收两个参数,第一个是要编辑的基本类型,第二个是你要删除的类型
例子:
type Person = {
name:stirng
sex:string
}
type newPerson = Omit<Person,'name'>//{sex:string}
type Omit<T,K extends keyof any> = Pick<T,Exclude<keyof T,K>>
2.Partial:可以快速把接口类型中定义的属性变成可选的(Optional)
例子:
type Person = {
name:string
sex:string
}
type newPerson = Partial<Person>
type Partial<T> = {
[P in keyof T]?: T[P]
}
3.Exclude:用于删除类型集合中的指定类型(有问题)
例子:
type a = string | number
type newPerson = Exclude<a,string>
type Exclude<T,U> = T extends U ? never : T
4.Readonly:将接口所有属性变为只读的
例子:
type Person = {
name:stirng
sex:string
}
type newPerson = Readonly<Person>
//type newPerson = {readonly name:string
type Readonly<T> = {readonly [P in keyof T]:T[P]}
5.ReturnType:适用于获取函数T的返回类型
例子:
type T0 = RetrunType<()=>string>
type T1 = ReturnType<(s:string)=>void>
type T2 = ReturnType<()=>T>
type T3 = ReturnType<()=>T>
type T4 = ReturnType
type T5 = ReturnType
type T6 = ReturnType
type T7 = ReturnType
type ReturnType any> = T extends (...args: any) => infer R ? R : any
类型守卫
1.类型守卫
const str = "linbudu";
const obj = { name: "linbudu" };
const nullVar = null;
const undefinedVar = undefined;
const func = (input: string) => {
return input.length > 10;
};
情况1:
type Str = typeof str;
let abc: Str = "linbudu";
type Obj = typeof obj;
let obj1: Obj = { name: "12" };
type Null = typeof nullVar;
let Null1: Null = null;
type Undefined = typeof undefinedVar;
let Undefined1: Undefined = undefined;
type Func = typeof func;
let func1: Func = (input: string) => {
return input.length > 0;
};
情况2:
const func = (input: string) => {
return input.length > 10;
};
const func2: typeof func = (name: string) => {
return name === "linbudu";
};
let isValid:typof isInputValid('linbudu');
情况3:
通过类型推导能力,它会随着你的代码逻辑不断尝试收窄类型
function foo(input:string){
if(typeof input === 'string'){};
if(typeof input ==== 'number'){};
}
注意点:
function isString(input:unknown):boolean{
return typeof input === 'string';
}
function foo(input:string | number){
if(isString(input)){
(input).replace('libudu','linbudu599');
}
if(typeof input === 'number'){
}
}
function isString(input:unknown):input is string{
return typeof input === 'string'
}
function foo(input:string | number){
if(isString(input)){
(input).replace('linbudu','linbudu599');
}
if(typeof input === 'number'){
}
}
因为isString这个函数在另外一个地方,内部判断逻辑不在函数foo中.这里的类型控制流分析做不到跨函数上下文来进行类型的信息收集
即isString函数称为类型守卫,在它的返回值中,我们不再使用boolean作为类型标注,而是使用is关键字来显示地提供类型信息
is关键字 + 预期类型:
表示这个函数成功返回true,那么is关键字前这个入参的类型,就会被这个类型守卫调用方后续的类型控制流分析收集到
2.in的类型保护
interface Foo{
foo:strig;
fooOnly:boolean;
shared:number;
}
interface Bar{
bar:string;
barOnly:boolean;
shared:number;
}
function handle(input:Foo | Bar){
if('foo' in input){
return input.fooOnly;
}else{
return input.barOnly;
}
}
3.instanceof类型保护
class FooBase{};
class BarBase{};
class Foo extends FooBase{
fooOnly(){}
}
class Bar extends BarBase{
barOnly(){}
}
function handle(input:Foo } Bar){
if(inpur instanceof FooBase){
input.fooOnly();
}else{
input.barOnly();
}
}
类型断言
语法一:值 as 类型
let anyValue:any = 'zhangsan';
let length1:number = (anyValue as string).length;
语法二:<类型>值
let anyValue:any = 'zhangsan';
let length1:number = (<string>anyValue).length;
函数
1.函数签名
方式一:
function foo(name:string):number{
return name.length;
}
方式二:
const foo:(name:string) => number = function(name){
return name.length;
}
方式三:
type FuncFoo = (name:string)=>number;
或
interface FuncFooStruct{
(name:string):number
}
const foo:FuncFoo = (name)=>{
return name.length;
}
2.void函数
function foo():void{
console.log(1);
}
3.可选参数和默认参数
function foo1(name:string = ‘name’,age?:number):number{
const inputAge = age || 18;
return name.length + inputAge;
}
function foo2(name:string,age = 18):number{
const inputAge = age;
return name.length +inputAge;
}
4.剩余参数
function foo(arg:string,...rest:[number,boolean]){
}
foo('linbudu',18,true);
5.重载
function func(foo:number,bar:true):string;
function func(foo:number,bar?:false):number;
function func(foo:number,bar?:boolean):string | number{
if(bar){
return String(foo);
}else{
return foo * 599;
}
}
const res1 = func(599);
const res2 = func(599,true);
const res3 = func(599,false);
重载的定义:
1.函数签名的定义:
函数签名=函数名称+函数参数+函数参数类型+返回值类型
2.函数签名的类型:
实现签名和重载签名
3.完整的函数重载定义:
3.1一个实现签名+一个或多个重载签名合成
3.2外部调用函数重载定义的函数时,只能调用重载签名,不能调用实现签名
3.3调用重载函数时,会根据传递的参数判断你调用哪个函数
3.4只有一个函数体,只有实现签名配备了函数体.所有的重载签名都只有签名,没有配备函数体.
3.5实现签名必须兼容所有重载签名该位置的参数类型
函数重载的例子:
type MessageType = "image" | "audio" | string;
type Message = { id: number; type: MessageType;sendmessage: string;};
let messages:Message[] = [
{
id: 1, type: 'image', sendmessage: "你好啊,今晚咱们一起去三里屯吧",
},
{
id: 2, type: 'audio', sendmessage: "朝辞白帝彩云间,千里江陵一日还"
},
{
id: 3, type: 'audio', sendmessage: "你好!张无忌"
},
{
id: 4, type: 'image', sendmessage: "刘老根苦练舞台绝技!"
},
{
id: 5, type: 'image', sendmessage: "今晚王牌对王牌节目咋样?"
}
]
function getMessage(value: number, myname: string): Message
function getMessage(value: MessageType, readRecordCount: number): Message[]
function getMessage(value: any, value2: any = 1) {
if (typeof value === "number") {
return messages.find((msg) => { return 4 === msg.id })
} else {
return messages.filter((msg) => value === msg.type).splice(0, value2)
}
}
getMessage(1, "df");
方法重载的例子:
class ArrayList{
constructor(public element:Array<object>){
}
get(index:number){
return this.element[index];
}
show(){
this.element.forEach((ele)=>{
console.log(ele);
})
}
remove(value:number):number;
remove(value:object):object;
remove(value:any):any{
this.element = this.element.filter((ele,index)=>{
if(typeof value === 'number'){
return value !== index;
}else{
return value !== ele;
}
})
return value;
}
}
let stuOne = {stuname:"wangwu",age:23};
let stuTwo = {stuname:"lisi",age:39};
let stuThree = {stuname:"liuqi",age:31};
let arrayList = new ArrayList([stuOne,stuTwo,stuThree]);
arrayList.show();
console.log('删除第一个学生');
arrayList.remove(stuTwo);
arrayList.show();
6.异步函数、Generator函数等类型签名
async function asyncFunc():Promise<T>{
}
function* genFunct():Iterable<void>{
}
async function* asyncGenFunc():AsyncIterable<void>{
}
类
class Foo{
prop:string;
constructor(inputProp:string){
this.prop = inputProps;
}
print(addon:string):void{
console.log(`${this.prop}and${addon}`);
}
get propA():string{
return `${this.prop}+A`;
}
set propA(value:string){
this.prop = `${value}+A`;
}
}
修饰符(public、private、protected、readonly):
class中属性默认为public
类和类的实例为两种概念,即一旦实例化完毕(出厂零件),那就和类(工厂)没关系,即不允许再访问受保护的成员.
public:此类成员在类、类的实例、子类中都能被访问
protected:此类成员仅能在类和子类中被访问
private:此类成员仅能在类的内部被访问
静态成员(static)
在类的内部,静态成员无法通过this来访问,需要通过Foo.静态成员名进行访问.
静态成员挂载在函数体上,而实例成员挂载在原型上.
继承、实现、抽象类
基类(父类)、派生类(子类)
1.在派生类中覆盖基类方法时,我们并不难确保派生类的这一方法能覆盖基类方法
使用override关键字,如果基类中没有这个方法,派生类中有这个方法,则会报错
抽象类:抽象类对类结构和方法的抽象(无法声明静态的抽象成员)
方式一:
abstract class AbsFoo{
abstract absProp:string;
abstract get absGetter():string;
abstract absMethod(name:string):string;
}
class Foo implements AbsFoo{
absPropr:string = 'linbudu';
get absGetter(){
return 'linbud';
}
absMethod(name:string){
return name;
}
}
泛型
1.映射类型:
type Factory<T> = T | number | string;
2.索引类型:
type Stringify<T> = {
K in keyof T]?:T[K]
}
3.条件类型:
type IsEqual<T> = T extends true ? 1 : 2;
type A = IsEqual<true>;//1
type B = IsEqual<false>;//2
type C = IsEqual<'linbudu'>;//2
4.泛型默认值:
type Factory<T = boolean> = T | number | string;
const foo:Factory = false;
5.泛型约束:使用extends进行约束
type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002 ? 'success' : 'failure';
type Res1 = ResStatus<10000>;//'success'
type Res2 = ResStatus<20000>;//'failure
type Res3 = ResStatus<'10000'>;//Type 'string' does not satisfy the constraint 'number'
6.多泛型关联:
//多泛型参数其实就像接收更多参数多函数,其内部的运行逻辑(类型操作)会更加抽象,表现在参数(泛型参数)需要进行的逻辑运算(类型操作)会更加复杂
type Conditional<Type,Condition,TruthyResule,FalsyResult> = Type extends Condition ? TruthyResult : FalsyResult;
//'passed!'
type Result1 = Conditional<'linbudu',string,'passed!','rejected!'>;
//'rejected!'
type Result2 = Conditional<'linbudu',boolean,'passed!','rejected!'>;