TypeScript语法学习记录

TypeScript:有类型系统得JavaScript超集

TS救急:
// @ts-nocheck 禁用整个文件的ts校验
// @ts-ignore 禁用单行ts校验

interface Hero {
  name: string;
  age: number;
  skill: string;
  skinNum?: number;
  say(): string; // say函数返回值为string
  [propname: string]: any; // 当前Hero可定义任意字符串类型的key
}
// 继承
interface littleSoldier extends Hero {
  rush(): string;
}
// 任意类型
interface IAnyObject {
  [key: string]: any;
}
----------------------------------------------------------------------------------


## 概念
**静态类型语言:**在编译阶段确定所有变量的类型
**动态类型语言:**在执行阶段确定所有变量的类型
![语言类型象限](https://upload-images.jianshu.io/upload_images/11685459-226b9de6506e944d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


##  一些英文
// type annotation  类型注解
// type inference    类型推断
// type Assertions  类型适配(类型断言)


## 一、TypeScript的数据类型

#####1.基本类型
Boolean:
Number:
String:
Symbol:
undefined:
null:   

#####2.复杂类型
Array:
Function:
Object: 

######2.1.复杂类型--(这些目前JS不具备)
void:空类型
any:任意类型
never:
Tuple:元组
enum:枚举

#####3.高级类型
union:组合类型
Nullable:可空类型
Literal:预定义类型

二、TypeScript 类型基础

基础静态类型:String、Number、Boolean、undefined、null、Symbol、void
对象静态类型:对象类型、数组类型、类类型、函数类型

2.1类型基础
//类型注解
let a:string="aaa"   
let b:number=2
let c:boolean=false
let union: string|number   //联合(Union)与文献(Literal)类型 ---一遍变量支持多种类型

let s1:symbol=Symbol();
const sym= Symbol();
let obj={
  [sym]:"semlinker",
}
console.log(obj[sym]); // semlinker 



//any 类型 
//any 类型本质上是类型系统的一个逃逸舱。这给了开发者很大的自由:TypeScript 允许我们对 any 类型的值执行任何操作,而无需事先执行任何形式的检查。
//使用 any 类型,可以很容易地编写类型正确但在运行时有问题的代码。如果我们使用 any 类型,就无法使用 TypeScript 提供的大量的保护机制。为了解决 any 带来的问题,TypeScript 3.0 引入了 unknown 类型。
let c:any=66;
c=false
c="123"



// unknown 类型--不保证类型,但保证类型安全
//unknown 类型只能被赋值给 any 类型和 unknown 类型本身。
let value: unknown;
value = true; // OK
value = 42; // OK
let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error

let randomValue: unknown =666;
randomValue={}/true/'str'   //等都不报错
randomValue() //unknown类型报错,any类型不报错
if(typeof randomValue === 'function'){
  randomValue() //加上类型校验后,unknown不报错了!!
}



//Void  类型与 any 类型相反,它表示没有任何类型。无的意思
// 声明函数返回值为void
function sayHello(): void {
  console.log('hello world');
  // return 'aa' //如果出现return/返回值 就会报错
}



//null 和 undefined 类型
//TypeScript 里,undefined 和 null 两者有各自的类型分别为 undefined 和 null。
let u: undefined = undefined;
let n: null = null;
//默认情况下 null 和 undefined 是所有类型的子类型。就是说你可以把 null 和 undefined 赋值给 number 类型的变量。然而,如果你指定了--strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们各自的类型。



// Never类型表示的是那些永不存在的值的类型。--永远执行不完。
//例如,never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}
function infiniteLoop(): never {
  while (true) {}
}




//数组类型
const nArr: number[] = [1, 2, 3];
let list:Array<number>=[1,2,3]  //Array<number> 泛型语法
let list:any[]=[1,2,3]  //可以存放任何类型数据
const sArr: string[] = ['a', 'b', 'c'];
const undefinedArr: undefined[] = [undefined, undefined, undefined];
const xiaojie7: [string, string, number][] = [
    ['dajiao', 'teacher', 28],
    ['liuying', 'teacher', 26],
    ['cuihua', 'teacher', 22],
];

const arr2: (number | string)[] = [1, 'string', 2];//联合类型数组,与元组容易混淆
const arr3: { name: string; age: number }[] = [
    { name: '刘', age: 18 },
    { name: '谢', age: 28 },
];




//Tuple  元组 类型  ---是一个特殊的数组,固定元素类型,固定长度
let c:[string, boolean]=["str",true] //增加第三个元素,或者改变某个项的数据类型都会报错。
//越界访问c[2]也会报错。
//但有BUG, 当执行c.push(3)时会成功,并且打印c数组,也已变成3个元素。




//type alias 类型别名      ----此处记录有问题,以后理解了要修改
type Lady = { name: string; age: number };
class Madam {
    name: string;
    age: number;
}
// const xiaojie4:Lady[] =[
const xiaojie4: Madam[] = [
    { name: '刘英', age: 18 },
    { name: '谢大脚', age: 28 },
];




//联合类型
let union:string|number|boolean
//约束了取值范围的 数字类型的联合  也可以叫 字面量类型(literal)
let union:0|1|3  

//混合使用案例-很强大
function merge(
  n1:number|string,
  n2:number|string,
  resultType: "as-number"|"as-string"
){
  if(resultType === "as-string"){
    return n1.toString() + n2.toString();
  }ele{
    return n1+n2
  }
}
merge(2,5,"as-number")  //7
merge(2,5,"as-string")  //25
merge("hello","world","as-string")  //helloworld
2.1.1 Function函数类型
//以下四个是等价的效果,四种函数的创建
// 形参可以使用可选参数,但必须放在必选参数之后
function add( x:number,y?:number ){ return y? x+y: x; }
let add: (x: number, y:number){ return x+y } 
let add: (x: number, y:number) => number
interface Add{
  (x: number, y:number): number
}
type Add = (x:number,y:number)=>number  //type 类型别名,给类型起个名字



// 函数注解
function getTotal(x: number, y?: number=0): number {
    console.log(x+ y)
}
getTotal(1)//参数2可写可不写

function add({ x, y}: { x: number; y: number }) {
    console.log(x+ y) 
}
add({ x: 1, y: 2 });

interface Point{
    x:number;
    y:number;
}
function add(point:Point) {
    console.log(point.x+ point.y) 
}
add({ x: 102, y: 215 });
2.1.2 object类型
//object, Object 和 {} 类型
//1.object类型是:TypeScript 2.2 引入的新类型,它用于表示非原始类型。

const proto = {};
Object.create(proto);     // OK
Object.create(null);      // OK
Object.create(undefined); // Error
Object.create(1337);      // Error
Object.create(true);      // Error
Object.create("oops");    // Error

//写法1:使用object关键词/{} 也可以,但没有任何意义
const obj:object={
  age:"小明",
  age:15
}
//写法2
const obj:{
  age:string,
  age:number
}={
  age:"小明",
  age:15
}


interface ObjectConstructor {
  create(o: object | null): any;
}
//2.Object 类型 :它是所有 Object 类的实例的类型,它由以下两个接口来定义:
//2.1Object 接口定义了 Object.prototype 原型对象上的属性;
interface Object {
  constructor: Function;
  toString(): string;
  toLocaleString(): string;
  valueOf(): Object;
  hasOwnProperty(v: PropertyKey): boolean;
  isPrototypeOf(v: Object): boolean;
  propertyIsEnumerable(v: PropertyKey): boolean;
}
//2.2ObjectConstructor 接口定义了 Object 类的属性。
interface ObjectConstructor {
  /** Invocation via `new` */
  new(value?: any): Object;
  /** Invocation via function calls */
  (value?: any): any;
  readonly prototype: Object;
  getPrototypeOf(o: any): any;
  // ···
}
declare var Object: ObjectConstructor;

//3.{} 类型  {} 类型描述了一个没有成员的对象。当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误。
2.2 类型适配(类型断言)Type Assertions
let msg:any;
msg="abc"; //此时变量的类型依然是any  而不是string,后续的赋值并不会改变其初始化时的类型

//适配方法1
let dd = (<string>msg).endsWith("c") //此时就可以使用string类型的方法了
//适配方法2
let dd = (msg as string).endsWith("c")
2.3 枚举类型
//Enum  枚举类型
1.数字枚举
enum Status {
  //初始值为 0,其余的成员会从 1 开始自动增长,也可以手动设置起始值
  // MASSAGE = 3, //从3开始,重置NORTH的初始值,也可以给每个值自定义顺序,使用字符串也可以
  MASSAGE,
  SPA,
  JIAN,
}
console.log(Status.MASSAGE); //0
console.log(Status.SPA);//1
console.log(Status.JIAN);//2

function getServe(status: number) {
    if (status === Status.SPA) {
        return 'message';
    } else if (status === Status.MASSAGE) {
        return 'SPA';
    } else if (status === Status.JIAN) {
        return 'jian';
    }
}
const result = getServe(0);



2.字符串枚举
//对于纯字符串枚举,我们不能省略任何初始化程序。而数字枚举如果没有显式设置值时,则会使用默认规则进行初始化。
enum Direction {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST",
}
let dirName = Direction[0]; // NORTH  ----?测一下
let dirVal = Direction["NORTH"]; // 0


3.常量枚举
//使用 const 关键字修饰的枚举,常量枚举会使用内联语法,不会为枚举类型编译生成任何JavaScript代码
//枚举类型---将程序中不容易记忆的硬编码或者是未来中可能改变的常量抽出来定义为枚举类型
const enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}
let dir: Direction = Direction.NORTH;
//对应的ES5代码
"use strict";
var dir = 0 /* NORTH */;

4.异构枚举---容易引起混淆,不建议使用
//异构枚举的成员值是数字和字符串的混合:
enum Enum {
  A,
  B,
  C = "C",
  E = 8
}
console.log(Enum.A) //输出:0
console.log(Enum[0]) // 输出:A


//不同的枚举类型不可进行比较
enum E {a,b}
enum F{a=0,b=1}
let e:E=3
let f:F=3
let ee:E=3
e===f      //报错
e===ee  //可以比较
高内聚:功能相关的事务应该放在同一个集合中形成一个模块;
低耦合:而这些模块又应该是互相独立的,不同的模块之间应该保持低耦合的状态
2.4 Interface
//interface  自定义 静态类型
interface person{
  age:number;
  sex:string;
}
const xiaohong:person{
  name:'小红',
  age:18,
}


interface Point{
    x:number;
    y:number;
}
function add(point:Point) {
    console.log(point.x+ point.y) 
}
add({ x: 102, y: 215 });
2.4.1 Interface
interface Waiter{
  anjiao:boolean;
  say:()=>{}
}

interface Teacher{
  anjiao:boolean;
  skill:()=>{}
}
//参数有多种情况的,叫联合类型    
//类型守护
function judgeWho(animal:Waiter|Teacher){
  if(animal.anjiao){
    (animal as Teacher).skill()  //断言
  }else{
    (animal as Waiter).say()
  }
  animal.say()
}


function judgeWhoTow(animal:Waiter|Teacher){
  if("skill" in animal){
    animal.skill()  //断言
  }else{
    animal.say()
  }
}


function add (first:string|number,second:string|number){
  if(typeof first==="string"||typeof second==="string"){
    return `${first}${second}`
  }
  return first + second
}
2.5 Class 类
对比例子1-1
interface IPoint{
    x:number;
    y:number;
    drawPoint:()=>void;
    getDistances:(p:IPoint)=>number;
}
class Point implements IPoint{
    //x,y叫做成员变量
    //x:number;   //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
    //y:number;
    // js的构造函数constructor 不能重载 overload
    constructor(x?:number,y?:number){  //变为可选参数后,在实例化时就可以不用传参数了
    // Access Modifier  访问修饰符
    constructor(public x:number,pbulic y:number=2){  
        //this.x=x;  //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
        //this.y=y;
    }
    drawPoint=()=>{
        console.log(this.x,this.y)
    };
    getDistances=(p:IPoint)=>{
        return Math.pow(p.x-this.x,2) + Math.pow(p.y-this.y,2)
    };
}
const point = new Point(2,3) //对象object,实例instance
//  point.x = 2
//  point.y = 3
point.drawPoint()

对比例子1-2
interface IPoint{
     //通常接口中的内容都是公开的,当constructor将变量设为私有后,这里会报错,简单处理可以把x删掉
    //x:number;  
    //y:number;
    drawPoint:()=>void;
    getDistances:(p:IPoint)=>number;
    getX:()=>number
    getY:()=>number
    setX:()=>void
    setY:()=>void
}
class Point implements IPoint{
    //x:number;   //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
    //y:number;
    // Access Modifier  访问修饰符
    constructor(private x:number,private y:number=2){  
        //this.x=x;  //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
        //this.y=y;
    }
    drawPoint=()=>{
        console.log(this.x,this.y)
    };
    getDistances=(p:IPoint)=>{
        //return Math.pow(p.getX()-this.x,2) + Math.pow(p.getY()-this.y,2)
    };
    setX=(val:number)=>{
        if(val<0){
            throw new Error("val不能小于0")//抛出异常
        }
        this.x=val
    }
    getX=(val:number)=>{
        return this.x
    }
    setY=(val:number)=>{
        if(val<0){
            throw new Error("val不能小于0")//抛出异常
        }
        this.y=val
    }
    getY=(val:number)=>{
        return this.y
    }
}
const point = new Point(2,3) //对象object,实例instance
point.setX(10)
point.setX(-9)
point.drawPoint()


//更简单的写法
//Typescript中提供的get  set用户
interface IPoint{
     //通常接口中的内容都是公开的,当constructor将变量设为私有后,这里会报错,简单处理可以把x删掉
    //x:number;  
    //y:number;
    drawPoint:()=>void;
    getDistances:(p:IPoint)=>number;
    X:number;
    Y:number;
    
}
class Point implements IPoint{
    //x:number;   //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
    //y:number;
    // Access Modifier  访问修饰符
    constructor(private x:number,private y:number=2){  
        //this.x=x;  //当constructor函数使用了访问修饰符时,系统会自动创建成员变量,并赋值
        //this.y=y;
    }
    drawPoint=()=>{
        console.log(this.x,this.y)
    };
    getDistances=(p:IPoint)=>{
        //return Math.pow(p.X-this.x,2) + Math.pow(p.Y-this.y,2)
    };
    //mooc称这种写法为getter、setter懒人包,
    //这种写法 编译器 的版本必须至少为ES5 或更高
    // tsc -t es5 app.ts
    set X(val:number){
        if(val<0){
            throw new Error("val不能小于0")//抛出异常
        }
        this.x=val
    }
    get X(val:number){
        return this.x
    }
    set Y(val:number){
        if(val<0){
            throw new Error("val不能小于0")//抛出异常
        }
        this.y=val
    }
    get Y(val:number){
        return this.y
    }
}
const point = new Point(2,3) //对象object,实例instance
point.X=10
console.log(point.X)
point.drawPoint()

2.5.1 类的访问类型

类的访问类型:pulbic private protected
//pulbic 类的内部 外部都可以使用---没有些访问修饰符的变量默认都是公开的
//private 私有的,只允许类的内部使用
//protected 外部不可用,但在继承的方法里可以访问

class Person3{
  public name:string;   //公用的,公众的,允许在类的内部和外部访问
  public sayHello(){
    console.log(this.name+'say hello')
  }
}
const person3 =new Person3()
person3.name='hi hi'


// interface  接口  里边必须是key,value的形式
interface Girl {
    name: string;
    age: number;
    bust: number;
    like?: string;   //?可有可无的意思
    [propname: string]: any; //属性名称是字符串类型,值是任何类型都可以
    say(): string; //接口中定义方法,返回值是string类型
}
// 接口继承     
//问题:查证子接口可否定义父接口中不存在的 注解
interface Teacher extends Girl {
    teach(): string;
}

// 类, 类受到 接口  约束的 写法
class xiaojiejie3 implements Girl {
    name = '小明';
    age = 18;
    bust = 77;
    say() {
        return 'hello';
    }
}

/* const screenResume = (name: string, age: number, bust: number) => {
    age < 24 && bust >= 90 && console.log(name + '进入面试');
    age >= 24 || (bust < 90 && console.log(name + '你被淘汰'));
}; */
const screenResume = (girl: Girl) => {
    girl.age < 24 && girl.bust >= 90 && console.log(girl.name + '进入面试');
    girl.age >= 24 || (girl.bust < 90 && console.log(girl.name + '你被淘汰'));
};



const girl = {
    name: '大脚',
    age: 18,
    bust: 94,
    like: '吃饭',
    say() {
        return 'hello';
    },
};
// screenResume('大脚', 18,89);
screenResume(girl);



//类 继承
class Lady1{
  content ='hi'
  sayHello(){
    return this.content
  }
}
const goddess = new Lady1()
console.log(goddess.sayHello())

// 类继承
class xiaojiejie4 extends Lady1{
  sayHello(){  //重写
    return super.sayHello()+'你好'       //通过super访问父类
  }
  sayLove(){
    return 'hi'
  }
}

const goddess2 = new xiaojiejie4()
console.log(goddess.sayHello())



//类的构造函数
class Person7{
 /* public name:string;
  constructor(name:string){
    this.name=name
  } */
  constructor(public name:string){}
}

class Teacher extends Person7{
  // 子构造函数继承父类时,必须在子类中调用父类的构造方法
  constructor(public age:number){
    // super()
    super('jspang')
  }
}

const person = new Person7('jspang')
console.log(person.name)  ///jspang

//get  set
class xiaojiejie{
  constructor(private _age:number){}
  get age2(){
    return this._age
  }
  set age2(age2:number){
    this._age=age2
  }
}
const dajiao12 = new xiaojiejie(28)
console.log(dajiao12.age2)


//静态类
class Girl2{
  static sayLove(){   //静态类 可以不用实例化,直接通过类名.方法名 拿到并使用
    return 'hi'
  }
}
console.log(Girl2.sayLove())

class Person2{
  public readonly _name:string
  constructor(name:string){
    this._name=name
  }
}
const person2 = new Person2('jspang') 
//person2._name='谢广坤'  //只读属性不允许修改
console.log(person2.name)

//抽象类
abstract class Girl5 {
  // abstract skill();
}

//子类都要实现一下 抽象类中的方法
class Waiter extends Girl5{
  skill(){
    console.log("倒水")
  }
}
class BaseTeacher extends Girl5{
  skill(){
    console.log("按摩")
  }
}
class SeniorTeacher extends Girl5{
  skill(){
    console.log("全身按摩")
  }
}
2.5.2 类的访问类型
interface List{
  readonly id:number:  //只读属性
  name:string;
  [x:string]:any; //字符串索引签名
  age?:number; //可写属性
}
interface Result{
  data:List[]
}
function render(result:Result){
  result.data.forEacha((value)=>{
    console.log(value.id,value.name)  
  })
}
let result={
  data:[
    {id:1,name:'A',sex:'male'},
    //有未规定的字段时,不报错。
    //TS采用了一种鸭式变形法。
    //鸭式变形发:如果一只鸟看起来像鸭子,游起来像鸭子,叫起来像鸭子。那这只鸟就可以看作鸭子。
    {id:2,name:'B'}
  ]
}
render(result)


interface StringArr{
  [index: number]:string //索引签名 --用任意的数字去索引StringArr都会得到一个string
  // y:number   //报错,不可以这样写

  [index: string]:string   //两种索引签名可以混用,需要注意的是数字签名的返回值要是字符串的子类型。
  
}



// 混合类型接口
interface Lib{
  ():void;
  version: string;
  doSomething(): void;
}

let lib:Lib= ()=>{}  //当给lib的属性不足时,ts会提示报错,缺少属性,这种情况下可以使用 类型断言
let lib:Lib= (()=>{}) as Lib;    //类型断言
lib.version='1.0';
lib.doSomething= ()=>{}
2.5 泛型
let arr:number[]=[1,2,3,4]
let arr:Array<number>=[1,2,3,4] //泛型

// 动态类型 泛型
let list=<T>(arr:T[]) => {
  return arr[arr.length-1]
}
list([1,2,3,4]) //动态判断类型
list(["a","b","c"])
list<string>(["a","b","c"])  //显式声明输入及输出的数据类型

//多泛型
let makeTuple = <T,Y>(x:T,y:Y) => [x,y]
const v1=makeTuple(1,"one");  //  会提示为(number|boolean)[]
const v2=makeTuple(true,1);   //    会提示为(boolean|number)[]
const v2=makeTuple<boolean,number>(true,1);  //  显式声明输入及输出的数据类型

//联合类型   --函数中的联合类型
function join(first: string | number, second: string | number) {}
//泛型   --函数中的泛型
function join2<jspang>(first:jspang, second:jspang) {
    return `${first}${second}`;
}

join2<string>("jspang","haha") 
 //前后两个值类型不同 不会报错,使用泛型可做到,但后类型不一致时报错提示
join2<number>(1,2)

//泛型 中数组的使用   --两种写法都可以
// function myFun<ANY>(params:Array<ANY>){
function myFun<ANY>(params:ANY[]){
  return params;
}
myFun<string>(['haha'])



// 通常泛型都使用T来书写
function join3<T,P>(first:T,second:P){
  return `${first}${second}`;
}
join3<string,number>('haha',2)




//类上使用泛型
class SelectGirl{
  constructor(private girls:string[]|number[]){}
  getGirl(index:number):string|number{
    return this.girls[index]
  }
}

const selectGirl = new SelectGirl(['大脚','刘英','小红'])
console.log(selectGirl.getGirl(1))



class SelectGirl2<T>{
  constructor(private girls:T[]){}
  getGirl(index:number):T{
    return this.girls[index]
  }
}

const selectGirl2 = new SelectGirl2<string>(['大脚','刘英','小红'])
console.log(selectGirl.getGirl(1))



//泛型中的继承
interface Meizi{
  name:string;
}


class SelectGirl3<T extends Meizi>{
  constructor(private girls:T[]){}
  getGirl(index:number):string{
    return this.girls[index].name
  }
}

const selectGirl3 = new SelectGirl3([
  {name:"大脚"},
  {name:"刘营"},
  {name:"小红"}
])
console.log(selectGirl.getGirl(1))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容