typescript
typescript 是js的超集,静态类型检测
命令:
//监听ts文件的变化
$ tsc -p tsconfig.json --watch
原始类型
为了使文件里的变量不污染全局
//在第一行增加
export {};
string
let str:string = "2" //string
number
let num:number = 1 //number
boolean
let bool:boolean = true //boolean
null
let nul:null = null; // null
undefined
let undef: undefined = undefined; // undefined
symbol
let sy: symbol = Symbol(); // symbol
void
let vd: void = undefined; // 可以把undefined类型赋值给void类型,但是反过来不行
// 函数没有返回值,那么函数的返回值类型就是void
function fn(): void {
return undefined;
}
非原始类型
object
不能是string,number,boolean,symbol,严格模式:多包括null,undefined
let obj7:object = {a: 1, b: '2'};//对象
let obj8:object = [1, 2, 3]; //数组
Object
Object是object类型子类型
包含了原始类型和非原始类型
代表所有拥有 toString、hasOwnProperty 方法的类型,所以所有原始类型、非原始类型都可以赋给 Object,严格模式下不包括null,undefined。{}空对象类型和大 Object 一样。
let a5:Object = 1
let a6:Object = []
let a7:Object = {}
let a8:Object = function(){}
{ }
{ }和Object范围类型一样
let a9:{} = 1
let a10:{} = [1,2,3]
let a11:{a:number,b:string} = {a:1,b:"1"}
let a12:{} ={}
数组类型
定义数组的方式
方式一:直接定义
let arr :number[] = [1,2,3]
arr[3] = 4
方式二:元组
// 元组:固定元素长度的数组,用数组的方式可以拓展
let arr1:[number,string,boolean] = [1,"2",true]
// arr1[3] = 3 //报错
arr1.push(2)
方式三:Array接口类型::泛型,类型参数化
Array<类型>
//例如:
let arr1: Array<number> = [1, 2, 3];
arr1.push('3'); // 报错
arr1.push(5);
let arr2: string[] = ['4', '5', 'a'];
arr2[3] = '6';
let arr3:Array<{}> = [1,"1",true]
let arr4:Array<Object> = [1,"1",true]
字面量类型
const
//定义的不可以修改
const a11:1 = 1
let str:"2" = "2"
// let str1:"2" = "1" //报错
let bool:true = true
TypeScript 支持 3 种字面量类型:string字面量类型、number字面量类型、boolean字面量类型
联合类型 |
用 | 表示可能的类型
let arr:(number | string | boolean)[] = [1,"2",true]
let a:number|string = 1;
a = "2"
交叉类型 &
用 & 表示 必须拥有全部
let a2:number & string //既要满足number类型,也要满足string类型的值 (没有一个这样的值)
let obj:{age:number} &{name:string} = {age:12,name:"张三"}
组合
let obj1:({age:number} | {height:200}) & ({name:string} | {wight:400}) = {
age:200,
name:"张三"
}
obj1={
age:160,name:"李四",height:200
}
any
指的是一个任意类型,它是官方提供的一个选择性绕过静态检测的作弊方式。非常不建议使用: (any是地狱)
let a:any =1;
a="2"
a={}
a.toFixed(1)
unknown
unknown 是typeScript 3.0中添加的一个类型,它主要用来描述类型并不确定的变量,和any的区别就是会进行类型检测。
let b:unknown;
b=1;
b="2";
b=undefined;
b={};
// b.toFixed(1)//报错,因为此时b的类型不确定
as 断言
// as 断言,确定b的类型
(b as number).toFixed(1)
never类型
never表示永远不会发生值的类型
function throwErrFn():never {
throw new Error('出错了');
}
// 1. **如果函数里是死循环,返回值类型也是never **
// 2. **never 是所有类型的子类型
接口 interface
定义一个类型
interface PersonInfoItf {
name:string;
age:number;
[key:string]:number | string;
}
let obj:PersonInfoItf={name:"",age:12}
let obj1:PersonInfoItf={name:"111",age:12}
let obj2:PersonInfoItf={name:"",age:12,a:1}
定义一个数组
let arr:(number| string)[] = [1,2,3,"4"]
interface ArrItf {
[idx:number] : number | string
}
let arr1:ArrItf = [1,2,3,"4"]
定义没有固定属性的对象接口类型
interface ObjItf{
[key :string] : string | number
}
let arr2:ObjItf = {
a:1,b:2,c:3
}
定义函数类型
let fn:() => void = ():void => {
console.log(11)
}
interface FnItf {
(a: string):void
}
let fn1:FnItf = (a):void => {
console.log(11)
}
//**很少使用接口类型来定义函数的类型,更多使用内联类型或类型别名配合箭头函数语法来定义函数类型;**
接口继承
多个不同接口之间是可以实现继承的,但是如果继承的接口PersonInfo和被继承的接口NameInfo有相同的属性,并且类型不兼容,那么就会报错。
多个接口组合成一个新的接口
interface PersonInfoItf{
name:string
}
interface OtherInfoitf {
age:number
}
interface MaleItf extends PersonInfoItf,OtherInfoitf{}
let person:MaleItf = {
name:"",
age:12
}
多个相同的接口
多个相同接口,相同名字的接口类型,里面的属性会进行合并
interface A {
name:string
}
interface A {
age :number
// name:number ; //报错,上面已经定义了
}
let a :A = {
name:"",
age:12
}
用处:扩展第三方库的接口
可缺省和只读属性修饰
可缺省:用 ?表示
只读:用 readonly 表示,写在只读的属性前面
interface B {
readonly name:string;
age?:number
}
let b:B = {name:""} //age 可缺省,有没有都行
// b.name = "123"//报错 ,因为name属性只读
let c:B = {name:"",age:12}
类型别名
interface接口类型不支持联合类型和交叉类型
类型别名 type 类型名 = 具体类型值
type ThreeTypes = string | number | boolean;
let a:ThreeTypes = 1
let b:ThreeTypes = "1"
let c:ThreeTypes = true
type PersonType = {name:string} & {age :number}
let a1:PersonType = {
name:"",
age:12
}
用处或者用法:类型别名可以针对接口没法覆盖的场景,例如组合类型、交叉类型等;
// 类型别名是不能拿重名
// type ArrType = Array<number> //报错 重名了
type ArrType = {[idx:number]:number | string}
// 函数定义
type Fntype = () =>number
let fn:Fntype = ()=>{return 1}
let fn1:() =>number = ()=>{return 1}
函数类型
函数类型定义
function fn():undefined{return undefined}
function fn1():void{};
function fn2(a:string,b:number):void{}
接口定义函数类型
interface FnItf{
(a:string,b:number):void
}
// let fn3:FnItf = (a,b) =>{}//a 和 b 代表的是参数
let fn3:FnItf = (b,a) =>{}
类型别名定义函数类型
type Fntype = ()=>{name:string}
let fn4:Fntype=()=>({name:""})
声明函数类型
declare function fn5():void;
函数的参数类型
// ?可选参数 意思是不传
function fn(a:string,b?:number){
}
fn("",1)
fn("")
// fn()//报错
// 设置函数参数默认值
function fn1(str:string="123"){
}
fn1()
fn1("123456")
// 剩余参数
// 解构
function fn2(...arr:number[]){}
fn2(...[1,2,3])
fn2(1)
fn2(1,2,3,4,5)
this
在Typescript中,必须要明确的指定this的类型(严格模式下)。
type ObjType = {name:string};
function fn(this:ObjType,a:string){
console.log(this);
}
let obj:ObjType = {name:""};
// fn.apply({})//报错 { } 值不符合ObjType的类型
fn.apply({name:""},[""])
fn.apply(obj,[""])
枚举
枚举的作用在于定义被命名的常量集合,一个默认从 0 开始递增的数字集合,称之为数字枚举。也可以指定值,这里可以指定的值可以是数字或者字符串。
enum StatusCode{
NotLogin = -1,
Success,
Exprie
}
//自动会补全+1 number类型
// 假设后端返回的状态码是变量res
let res:number = 0;
if(res===StatusCode.NotLogin){
console.log("没登录");
}else if(res===StatusCode.Success){
console.log("登录成功");
}else if(res===StatusCode.Exprie){
console.log("登录已过期");
}
console.log(StatusCode.Success,StatusCode.Exprie);
泛型
指的是类型参数化,即将原来某种具体的类型进行参数化。
let arr:Array<number | boolean | string> = [1,true,""]
let arr1:Array<number>=[1];
function fn(a:unknown){
}
fn(1)
fn("")
fn(true)
function fn1(p:number):number{
return p
}
function fn2(p:string):string{
return p
}
function fn3(p:boolean):boolean{
return p
}
type parmsType = number | string | boolean;
function fn5(p:parmsType):parmsType{
return p
}
泛型函数定义
function fnn<S,P=string>(p:P,s:S):P{
return p
}
fnn<number>("",1)
fnn<string>("","")
type ObjType = {n:string}
fnn<ObjType,number>(1,{n:""})
fnn("","")// fnn<string, "">(p: "", s: string): ""
泛型类型
// 定义数组类型
let arr: Array<number> = [1];
let arr1: Array<string> = [""];
// 类型别名
type typeFn<P> = (params: P) => P;
let fntype: typeFn<number> = (n: number) => {
return n;
};
let fn1:typeFn<string> = (p: string):string => {
return p;
}
// 定义接口类型
interface TypeItf<P> {
name: P;
getName: (p: P) => P;
}
let t1: TypeItf<number> = {
name: 123,
getName: (n: number) => {
return n;
},
};
let t2: TypeItf<string> = {
name: "123",
getName: (n: string) => {
return n;
},
};
泛型约束
把泛型入参限定在一个相对更明确的集合内,以便对入参进行约束。
// T只接受number & string
interface ObjItf<T extends string | number> {
name:T;
getName:(n:string)=>string
}
let obj:ObjItf<number> ={
name:1,
getName:(n:string)=>{
return n
}
}
类的定义使用
// 在定义类的同时,除了定义的这个类,也创建了一个接口,接口的名字就是类名Person
class Person{
// 成员属性:类型
name:string="张三"
}
let p:Person = new Person();
let obj:Person={
name:""
}
class Male {
name:string;
age:number;
constructor(name:string ,age:number){
this.name = name;
this.age = age
}
say(this:Male,song:string){
console.log(song);
return this
}
}
let m = new Male("张三",20)
console.log(m.say("唱歌").name);
console.log(m.name,m.age);
let m1 = new Male("李四",100)
console.log(m1.say("跳舞").name);
console.log(m1.name,m1.age);
继承
使用extends关键字实现继承
// 类中常用修饰符
// public:公用的,默认类中的属性和方法都是public修饰的,基类,子类,类外部都可以访问
// protected:受保护的,基类,子类都可以访问,类外部不可以访问
// private:私有的,基类可以访问,子类,类外部都不可以访问
// readonly:只读(不能修改)
class Person{
public name:string;
protected readonly weight:string="50kg";
private heigth:string = "150";
constructor(name:string){
this.name = name
}
say(){
// this.weight = "" ;只读属性不能被修改
// 可以在当前的类中访问自己的public修饰的成员属性
console.log(this.name,this.weight,this.heigth);
}
}let p = new Person("张三")
p.say()
console.log(p.name);
// 受保护和私有的属性不能在类的外部访问,p.weight
class Male extends Person{
age:number;
constructor(name:string,age:number){
super(name);
this.age = age
}
say(): void {
console.log("子类"+ this.name + this.weight);
// 子类不能访问父类的private修饰的属性
}
}
let m = new Male("李四",22)
console.log(m.name,m.age);
静态属性
基于静态属性的特性,往往会把与类相关的常量、不依赖实例 this 上下文的属性和方法定义为静态属性,从而避免数据冗余,进而提升运行性能。
class Person{
name!:string;
// 静态成员
static readonly age:number = 10;
}
let p = new Person();
console.log(p.name);
// 访问静态成员
console.log(Person.age);
抽象类和接口
抽象类,它是一种不能被实例化仅能被子类继承的特殊类。
abstract class Person1{
abstract name:string;
abstract say():void;
run(){
console.log("跑");
}
}
class Male extends Person1{
name: string = "zhsngan";
say(): void {
console.log("抽象方法say");
}
}
let m = new Male();
m.name;
m.say();
m.run();
// 接口
interface Person2{
name:string;
age:number;
say:()=>void
}
class Female implements Person2{
name: string = "";
age: number= 20;
weight:string = "200"
say(){}
}
// class F1 implements Person2 {
// name: string;
// age: number;
// say: () => void;
// }
修饰符
public、private、protected、readonly
在 TypeScript 中就支持 3 种访问修饰符,分别是 public、private、protected。通过这三个修饰符做到控制属性和方法的访问。
- public:基类、子类、类外部都可以访问;
- protected:基类、子类可以访问,类外部不可以访问;
- private:基类可以访问,子类、类外部不可以访问;
- readonly:只读修饰符
class Person {
public readonly name: string = '张三';
protected age: number = 20;
private height: string = '180';
protected getPersonInfo():void {
console.log(this.name, this.age, this.height); // 基类里面三个修饰符都可以访问
}
}
class Male extends Person {
public getInfo():void {
console.log(this.name, this.age); // 子类只能访问public、protected修饰符的
}
}
let m = new Male();
console.log(m.name); // 类外部只能访问public修饰的
m.name = '李四'; // name属性使用只读修饰符,所以不能对name进行赋值修改操作
工具类型
Required:必填
Readonly:只读
extends: 约束
keyof:提取对象属性名、索引名类型
in :类型映射
typeof : 提取变量类型
interface PersonItf{
name:string;
age?:number;
}
// interface PersonItf1{
// name:string;
// age:number;
// }
// 重复了
// Required:必填
type PersonItf1 = Required<PersonItf>
// Readonly:只读
type PersonItf2 = Readonly<PersonItf>
// extends: 约束
type TypeFn<P> = P extends string | number ?P[]:P;
let a1:TypeFn<string> = ["111"]; //a1类型:string[]
let a2:TypeFn<boolean> = true; //a1类型:string[]
// infer:类型推断
type TypeFn1<T> = T extends {name:infer N;age:infer A} ?[N,A]:[T]
let a3:TypeFn1<{name:string,age:number}> = ["",2]
let a4:TypeFn1<boolean> = [false]
// keyof:提取对象属性名、索引名类型
interface ObjItf{
name:string;
age:number;
// [idx:number]:number;
[key:string]:string | number;
}
type TypeKeyof = keyof ObjItf //"name" | "age" | string | number
let a1:TypeKeyof="name"
a1=2
a1=""
// in :类型映射
type numAndStr = number | string
type ObjType1 = {[key in numAndStr]:numAndStr}
let ob1:ObjType1 = {
name:"",
1:2
}
// keyof in 组合使用 "name" | "age" | string | number
type Required<T> = {[P in keyof T]-?:T[P]}
type ItfType = Required<ObjItf>
// typeof : 提取变量类型
let a2 =1;
type TypeA2 = typeof a2
let obj2 = {
name:"",
age:20
}
type ObjType2 = typeof obj2;
let obj3:ObjType2 = {
name:"",
age:10
}