Typescript

从本文你能理解到那些常识

  • 强类型和弱类型
  • 动态类型和动静类型
  • JavaScript自有类型零碎的问题
  • Flow动态类型查看计划
  • TypeScript 语言标准和根本应用

强类型与弱类型 (类型平安)

TypeScript 是一门基于 JavaScript 之上的编程语言,它重点解决了 JavaScript 语言自身类型零碎有余的问题。

强类型:具备类型束缚 不容许隐式类型转换 束缚后,不合乎类型的编译阶段就会报错,而不是执行阶段

弱类型:不具备类型束缚 容许隐式类型转换

动态类型和动静类型

动态类型 一个变量申明时他的类型就是明确的,并且不可批改

动静类型 运行阶段才能够明确变量类型,而且变量的类型随时能够扭转

js 弱类型产生的问题

const obj ={}obj.foo()  //只有运行阶段才发现错误//如果谬误放异步中,就会存有隐患setTimeout(() => {  obj.foo()}, 1000000);
//类型不明确导致无奈确定函数性能function sum (a,b){  return a+b}console.log(sum(1,2));console.log(sum("100",100));

相比于弱类型,强类型的劣势

  • 谬误更早的裸露
  • 代码更智能,编码更精确 依据强类型的智能提醒
  • 重构更可靠
  • 缩小不必要的判断

Flow JS类型查看器

2014 facebook 类型注解可控不是都必须加

  • flow装置

    npm install flow-bin -Dnpx flow init  //生成.flowconfig

    应用flow时敞开vscode的语法校验,因为增加的类型注解不是规范的js语法

    文件 -> 首选项 -> 设置 -> javascript validate

    增加正文@flow,标记进行flow查看的文件

控制台应用 yarn flow 会查找node_modules里的.bin/flow进行类型查看

//@flowfunction sum(n:number,m:number){  return n+m}// console.log(sum("100",100)) //"100"会报错console.log(sum(100,200))

yarn flow stop 完结服务

Flow 移除注解

@flow并不是js的语法。因而执行 node +文件.js 或浏览器中 会在控制台报错

编码过后主动移除掉类型注解

  • 形式1 flow官网移除

    • 装置:yarn add flow-remove-types --dev
    • 应用:yarn flow-remove-types . -d dist 点(改成src)是所有文件 前面是输入地位,作用是去除类型注解,在node和浏览器中应用
    • 会在dist生成对应js文件
  • 形式2 babel插件

    • 装置:

      npm i @babel/core @babel/cli @babel/preset-flow -D

      yarn add @babel/core @babel/cli @babel/preset-flow --save

    • 应用:yarn babel src/part2_typescript/ts_typescript_06.js -d dist/js
    • yarn babel src -d dist 编译src文件用babelflow

Flow vscode插件

  • Flow Language Support,益处是在编译阶段就报错下面都是运行后控制台报错

Flow 类型推断

  • 倡议还是增加类型注解

    function square(n){return n * n} // square("100") //n报错let num:number = 1// num = "streing"

    Flow类型注解类型

  • string number boolean null undefined symbol
  • array object function
const a:string = "1"const b:number = NaN//100, Infinityconst c:boolean = trueconst d:null = nullconst e:void = undefinedconst f:symbol = Symbol()const arr:Array<any> = [1,true]const arr1:any[] = [1,true]const fw:[string,number] = ['foow',1] //固定长度类型的数组成为元组const obj_:{foo?:string,bar:number} = {foo:"1as",bar:1} //能够可选const obj:{[string]:string} = {} //通过key给空对象增加,约定key和valueob.key1 = "value"function fn(callback:(string,number)=>void){ //函数参数指定函数  callback("str",1)}fn(function(str,n){})

非凡类型

//字面量 const wc:"foo" = "foo"//字面量配合联结类型const type:'success' | "warning" | "danger" = "success"const nb: string | number = 1//类型别名type StringOrNumber = string | numberconst sb:StringOrNumber = "stri"
//MayBe类型,在具体类型根底上扩大了null和undefined两个值,增加?const gender:?number = nullconst gender1:number | null | void = undefined //同上//Mixed Any 任意类型 所有类型的联结类型 any是弱类型,mixed是强类型//string | number | booleanfunction passMix(value:mixed){  // value * value  报错  // value.split(",") 报错  if(typeof value === 'string'){ //这个联结类型须要判断    value.split(",")  }  if(typeof value === 'number'){    value * value  }}function passAny(value:any){  value * value  value.split(",")}

typescript

ts官网

  • 装置

    npm i typscript -Dnpm i ts-node -Dtsc --init

    tsc xxx

    ts-node xxx

相比拟flow 生态健全弱小,欠缺 且渐进式的,什么不会也齐全当作js用

毛病是多了很多概念,接口泛型枚举

不同文件同名抵触的问题。js不会报错会笼罩,然而ts会在显示阶段就揭示你报错,
因而,通过IIFE或者 export{}模式将文件造成公有作用域

//独自作用域// (function(){  const hello = (name:string) =>console.log(`Hello,${name}`);  hello("TypeScript")  //原始类型   //string,number,boolean都能够设置成null or undefined  const a:string = "aa"  const b:number=1//NaN  Infinity  const c:boolean= false  const d:string = null  const e:void = undefined //null  const f:null = null  const g:undefined = undefined  const h:symbol = Symbol()  //})()//改成模块,所有成员都是部分成员,不是导出了个对象export {}

首选项 typescript local -> zh-CN批改成中文谬误提醒

object类型

//Object类型不单指对象,而是原始类型以外的其余类型,//`number,string,boolean,symbol,null或undefined`之外的类型const foo:object = function (){}//{},[]const obj:{foo:number,bar:string} = {foo:123,bar:"as"}//这种模式能够用interface搞定
declare function create(o: object | null): void;create({ prop: 0 }); // OKcreate(null); // OKcreate(42); // Errorcreate("string"); // Errorcreate(false); // Errorcreate(undefined); // Error

array类型

const arr:Array<number> = [1,2,3]const arr2:number[] = [1,2,3]function sum(...args:number[]){  return args.reduce((executor,current)=>{    return executor+current  })}console.log(sum(1,2,3))

元组

//明确元素数量和元素类型的数组const tuple:[number,string] = [1,"1"]tuple[0]  const [age,name] = tuple

枚举

const status = {  Before:0,  Doing:1,  After:2}//默认从0开始累加,枚举会影响咱们编译后的后果(看下编译的js文件), enum Status{  Before,  Doing,  After  // After = "aff" //字符串枚举,不常见}// const enum Status{ //加const为常量枚举//   Before,//   Doing,//   After// }const s = {  current:Status.Doing}console.log(s);//有const 不能通过索引拜访枚举名console.log(Status[0])

函数类型

//函数类型function sth(a:number,b?:number,c:number=10):string {  return ""}function sth1(a:number,...args:number[]):string {  return ""}const sth2:(a:number,b:number)=>string = function (a:number,b:number):string {  return ""}

任意类型

//any不会做类型查看,语法上不会报错function sth3(s:any){}

隐式类型推断

//(倡议增加明确类型)let num = 18// num = "" //类型谬误let fx   //any类型

类型断言

//并不是类型转换,代码编译过后不存在了就const nums = 1;const mn = nums as numberconst mn2 = <number>nums  //jsx 不反对

接口 interface

约定一个对象有哪些成员,这些成员类型是什么

接口就是用来束缚对象的构造,一个对象实现一个接口他就必须领有接口所束缚的成员

接口只是类型束缚,运行阶段就没了

interface Person{  age:number //逗号,分号,不加  name:string  sex?:string  readonly height:number  [key:string]:any}function student(s:Person) {  console.log(s.age);  console.log(s.name);}const stud:Person = {  age:1,  name:"as",  height:199}// stud.height = 180stud.va = "asd"stud.as = 112

函数类型接口

interface SearchFunc {  (source: string, subString: string): boolean;}
let mySearch = <SearchFunc>function(source: string, subString: string) {  let result = source.search(subString);  return result > -1;}

类类型接口

interface ClockInterface {    currentTime: Date;    setTime(d: Date);}class Clock implements ClockInterface {    currentTime: Date;    setTime(d: Date) {        this.currentTime = d;    }    constructor(h: number, m: number) { }}

类动态局部与实例局部的区别

当你操作类和接口的时候,你要晓得类是具备两个类型的:动态局部的类型和实例的类型。 你会留神到,当你用结构器签名去定义一个接口并试图定义一个类去实现这个接口时会失去一个谬误:

interface ClockConstructor {    new (hour: number, minute: number);}class Clock implements ClockConstructor {    currentTime: Date;    constructor(h: number, m: number) { }}

这里因为当一个类实现了一个接口时,只对其实例局部进行类型查看。 constructor存在于类的动态局部,所以不在查看的范畴内

因而,咱们应该间接操作类的动态局部。 看上面的例子,咱们定义了两个接口, ClockConstructor为构造函数所用和ClockInterface为实例办法所用。 为了不便咱们定义一个构造函数 createClock,它用传入的类型创立实例。

interface ClockConstructor {    new (hour: number, minute: number): ClockInterface;}interface ClockInterface {    tick();}function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {    return new ctor(hour, minute);}class DigitalClock implements ClockInterface {    constructor(h: number, m: number) { }    tick() {        console.log("beep beep");    }}let digital = createClock(DigitalClock, 12, 17);

接口继承

interface Shape {    color: string;}interface PenStroke {    penWidth: number;}interface Square extends Shape, PenStroke {    sideLength: number;}let square = <Square>{};square.color = "blue";square.sideLength = 10;square.penWidth = 5.0;

class类

形容一类具体事务的形象特色 (手机)

申明,修饰符,构造函数,静态方法

class Person{  name:string //要申明相比js(es7)  private age:number //默认public  protected gender:boolean  readonly sex:string = "as" //只读  constructor(name:string,age:number){    this.name = name    this.age = age  }  say(msg:string):void{    console.log(`hi ${this.name},${msg}`);  }}const mc = new Person("mcgee",18)mc.name// mc.age// mc.gender
class Student extends Person{  constructor(name:string,age:number){    super(name,age)    console.log(this.gender); //可拜访      }}
class Student2 extends Person{  private constructor(name:string,age:number){ //构造函数增加private  则无奈实例化,无奈被继承    super(name,age)    console.log(this.gender);  }  static create(name:string,age:number){    return new Student2(name,age)  }}// new Student2("MC",16) //错Student2.create("MC",16) //能够//给构造函数增加protected 无奈被实例化,然而能够被继承

类与接口

类与类之间有公共特色,通过接口形象 (手机和座机都能打电话)
interface Eat{  eat (food:string):void}interface Run{  run (distance:number):void}class Person implements Eat,Run{ //类实现了接口必须有接口的成员  eat(food:string){  }  run(distance:number){  }}// class Animal implements EatAndRun{//   eat(food:string){//   }//   run(distance:number){//   }// }

抽象类

相似于接口,束缚子类中必须有某个成员,区别是抽象类能够蕴含一些具体的实现,接口只能是成员的形象不蕴含具体实现

抽象类只能被继承,不能new

abstract class Animal{  eat(food:string):void{    console.log(food);  }  abstract run(distance:number):void //形象办法不须要办法体}class Dog extends Animal{  run(distance:number):void{  }}const d = new Dog()d.eat("sss")d.run(100)

泛型

定义函数,接口或类的时候没有指定具体类型,等到咱们应用的时候再去指定具体类型

目标是极大水平的复用代码

function  arraynumber(length:number,value:number) :number[]{  return Array<number>(length).fill(value)}const res = arraynumber(3,100) //[100,100,100]//如果再定一个arraystringfunction  arraystring(length:number,value:string) :string[]{  return Array<string>(length).fill(value)}//把定义时不能明确的类型变成参数,应用时传递function createArray<T>(length:number,value:T):T[]{  return Array<T>(length).fill(value)}createArray<string>(4,"mcgee")

泛型类型

function identity<T>(arg: T): T {    return arg;}let myIdentity: <U>(arg: U) => U = identity;
//泛型类型接口interface GenericIdentityFn {    <T>(arg: T): T;}function identity<T>(arg: T): T {    return arg;}let myIdentity: GenericIdentityFn = identity;
//绑定了接口泛型类型interface GenericIdentityFn<T> {    (arg: T): T;}function identity<T>(arg: T): T {    return arg;}let myIdentity: GenericIdentityFn<number> = identity;

泛型类

class GenericNumber<T> {    zeroValue: T;    add: (x: T, y: T) => T;}let myGenericNumber = new GenericNumber<number>();myGenericNumber.zeroValue = 0;myGenericNumber.add = function(x, y) { return x + y; };

泛型束缚

//泛型T满足接口length属性interface Lengthwise {    length: number;}function loggingIdentity<T extends Lengthwise>(arg: T): T {    console.log(arg.length);  // Now we know it has a .length property, so no more error    return arg;}

类型申明

引入第三方模块时候,如果第三方模块不蕴含类型申明,能够自申明

引入 query-string

import {camelCase} from 'lodash' //@types/lodashimport qs from 'query-string'declare function camelCase (input:string):string //增加后res:stringconst res = camelCase('hello camelCase') //res:anyqs.parse("?key=asdasdq122") //自带类型束缚