关于TypeScript,我想说

时间:2021-1-8 作者:admin

哈哈,其实只是学习📒

可以提高代码的可靠程度

类型系统

强类型和弱类型(类型安全)

强类型有更强的类型约束,而弱类型中几乎没有约束
强类型不允许任意的数据隐式类型转换,而弱类型则允许任意的数据隐式类型转换
语言层面限制
javascript是一门弱类型/动态类型的语言

弱类型的问题

运行阶段才会发现异常,隐式类型转换可能会引发一系列的问题,强类型语法阶段就不能通过

强类型的优势

错误更早暴露–编码阶段,语言就能表现出来
代码更智能,编码更准确
重构更牢靠
减少不必要的类型判断

静态类型与动态类型(类型检查)

是否允许随时修改变量的类型

Flow

javascript类型检查器

快速上手

先要在文件夹中npm init -y
安装flow-bin,这个是flow的类型检查工具模块
应用flow的文件开头加上注释// @flow
给函数参数加上类型注释

注意: 使用flow的时候,如果你是用的是vs code那么需要在setting=>搜索JavaScript Validate,来关闭这个自动检测工具,安装Flow Language Support插件

npm

package.json的

function sum(a: number, b: String){} // 这就是类型注解

编译移除类型注解

flow-remove-types或者@bable/core @bable/cli @bable/preset-flow两种工具移除注解

以npm/bable为例:

npm install --save-dev @babel/core @babel/cli @babel/preset-flow
然后新建一个.babelrc文件,配置presets
{
  "presets": ["@babel/preset-flow"]
}
如果你把你所有的源文件都放在了src目录下,编译输出目录为dist目录,那么可以运行以下命令

./node_modules/.bin/babel src/ -d dist/

或者可以在package.json中配置script
{
  "name": "my-project",
  "main": "lib/index.js",
  "scripts": {
    "build": "babel src/ -d dist/",
  }
}

flow开发工具插件

Flow Language Support

Flow的各种类型

Flow 原始类型

和javascript原始类型一致 number string boolean undefined null Symbol bigInt

Flow数组类型
const arr1: Array<number> = [1, 2, 3];
const arr2: number[] = [1, 23, 4];

// 元组
const foo: [string, number] = ['foo', 100];
Flow对象类型
const obj1: { foo: string, bar: number } = { foo: "string", bar: 100 };
const obj2: { foo?: string, bar: number } = { bar: 100 };
// 声明的时候,可以为空对象,但是定义的数据类型还是要遵守
const obj3: { [string]: string | number } = {};
obj3.key1 = "value1";
obj3.key2 = 100;
Flow函数类型

参数类型在参数后面添加类型注解,对于函数的返回值的类型在函数括号后面添加类型注解

// 定义函数的回调函数的参数类型和返回值类型
function foo(callBack:(string,number)=>void){
  callBack('100',200)
}
Flow特殊类型
// 字面量类型
const a: "foo" = "foo";
const type: "success" | "warning" | "danger" = "success"; 

// 或类型、联合类型
type StringORNumber = string | number; 
// 使用type关键字,type相当于给类型做一个别名 ,单独声明一个类型

const b: StringORNumber = 100;
const b1: StringORNumber = "100";

// MayBe 类型
const gender: ?number = undefined;
const gender1: ?number = null;
const gender2: ?number = 1000;
Flow Mixed 与 any

mixed 所有类型的联合类型 any 都懂吧,不说了
mixed是强类型的 当定义为mixed类型的参数,没有明确定义为某一种类型,是不能当做那一种类型的方式去使用的

例如:
function passMixed(value: mixed){
  if(typeof value === 'string'){
    value.substr(1)
  }
  if(typeof value === 'number'){
    value * value
  }
}

而any 是弱类型的

Flow类型小结

了解常见的,具体的可以去查询

运行环境API

const element: ?HTMLElement = document.getElementById("app");
flow官方库中可以找到

TypeScript

属于渐进式的,任何一种js的运行环境都支持,相较于flow,功能更为强大,生态更健全
缺点:

  1. 语言本身多了很多概念(接口,泛型,枚举等),提高学习成本
  2. 项目初期,TS会增加一些成本
快速上手

1.安装ts模块,会提供tsc命令

npm init -y
npm i TypeScript --save-dev  

2.package.json中

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "tsc": "tsc"
  },

3.新建一个ts结尾的ts文件,就可以愉快的开发了
4.编译
npm run tsc 文件路径

配置文件

全局安装typescriptnpm i typescript -g,然后tsc --init初始化tsconfig.json文件,生成一个基本的ts配置文件

TS数据类型

原始数据类型
const  a:string ="foobar"
const b: number = 100   // NAN Infinity
const c: boolean = true

const d:void = undefined  //null 
const e:null = null
const f:undefined =undefined
const g :symbol = Symbol()  

tsconfig文件中"target": "es2015"才能使用; "target": "es5"时,使用symbol 也会报错,所有的es6语法都会报错,因为这个设置使得ts默认使用的标准库是es5所对应的标准库;如果我们一定要使用”target”: “es5″时,同时也要兼容es6时,这个时候我们就需要使用tsconfig文件中的另一个lib 属性,我们就要来认识标准库的概念

标准库

"lib": ["ES2015"]当这么设置时,会覆盖所有其他的默认标准库,所以要添加"lib": ["ES2015","DOM"],ts中把bom和dom归为一个DOM标准库

标准库就是内置对象所对应的声明文件,我们在代码中使用内置对象,就必须要引用对应标准库,否则ts会找不到对应的类型

TypeScript中文错误消息

如果我们使用的是英文版本的话,tsc --locale zh-CN命令行输入这个命令,可以使错误消息改成中文

TypeScript作用域的问题

不同文件中声明同名变量时,可能会引起作用域问题,ES module、自执行函数等可以处理作用域问题

TypeScript的Object类型

指的不是普通的对象类型,二十泛指所有的非原始类型,包括对象,数组还有函数等

const foo: object = function () { }  // [] //{}
// 如果声明普通对象类型,可以使用类似对象字面量语法的方式,也可以使用时接口的方式
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
TypeScript的数组类型类型
const arr1: Array<number> = [1, 2, 3]   // Array 泛型
const arr2: number[] = [1, 2, 3]
TypeScript的元组类型

元组就是明确元素数量和元素类型的数组
const tuple :[number,string] =[100,'str']

TypeScript的枚举类型
enum PostStatus {
    Draft = 0,
    Unpublished = 1,
    Published = 2
}
enum PostStatus1 {  // 不指定,默认从0开始累加
    Draft,
    Unpublished,
    Published
}
enum PostStatus2 {  // 指定第一个,默认从这个基础上开始累加
    Draft = 6,
    Unpublished,
    Published
}
enum PostStatus3 {  // 字符串枚举的无法累加,要一一指定
    Draft = 'qqqq',
    Unpublished = "wwww",
    Published = "eeee"
}

常量枚举

const enum PostStatus {  // 就是加一个const
    Draft = 0,
    Unpublished = 1,
    Published = 2
}
TypeScript的函数类型

就是对函数的输入输出进行类型的限制
函数的声明

// 函数声明
function func1(a: number, b?: number, ...rest: number[]): string {   // ?可选参数,和默认参数  都要放在参数最后   ...rest  接受任意个数的参数
    return 'func1'
}
func1(100, 200, 300)

函数表达式

// 函数表达式
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
    return 'func2'
}
TypeScript的任意类型

any,动态类型,有类型安全问题,在运行过程中可以存放其他的值,意思是声明的赋值是number类型,使用的时候赋值了一个string类型的值,是不会报错的,应为TypeScript不会对any 类型的值去做类型检查

TypeScript的隐式类型推断
let age = 18  // 就会推断age为number类型
let age   // 就会推断为any 类型
TypeScript类型断言

as 语句

// 假定这个nums 来自一个明确的接口
const nums = [100, 120, 130, 140]
const res = nums.find(i => i > 0)   //  ts推断res: number | undefined
// 在后面的操作中 可以用as来断言  res一定是一个number 不会是undefined
const num1 = res as number
// 或者
const  num2 = <number>res    // JSX中,<number>会跟html标签冲突,不能使用
TypeScript的接口 interface

接口就是用来约束对象的结构,一个对象实现一个接口,就必须拥有这个接口中定义的所有的成员
编译完成后的文件,没有任何关于接口相关的代码
ts中的接口是为我们有结构的数据做类型约束的,在实际运行阶段是没有意义的

interface Post {
    title: string
    content: string
}

function printPost(post: Post) {
    console.log(post.title);
    console.log(post.content);
}

接口成员 可选成员、只读成员、动态成员

interface Post {
    title: string
    content: string
    subtitle?: string   // 可选成员
    readonly summary: string  // 只读成员,不能修改
}


interface Cache {        // 动态成员定义
    [key :string]: string   // 定义的时候不知道类型,可以这么定义,这里键值都是string类型
}
// 举例
const cache : Cache = {}
cache.foo = 'value1'
TypeScript的类的使用

类的作用 描述一类具体事物的抽象特征
类 用来描述一类具体对象的抽象成员
TS增强了class的相关语法

// 类的属性在使用之前必须要在类型中去声明,目的是给属性做类型标注
class Person {
    name: string
    age: number = 18  // 可以在这里赋值一个初始值,但是一般我们都是在构造函数中动态的去赋值,ts中类的属性必须要有一个初始值
    constructor(name: string, age: number) {
        // 在ts中需要在类中声明某个属性
        this.name = name
    }
    sayHi(msg: string): void {
        console.log(`I am ${this.name},${msg}`);
    }
}
类的访问修饰符
class Person {
    name: string
    pri age: number = 18  // 可以在这里赋值一个初始值,但是一般我们都是在构造函数中动态的去赋值,ts中类的属性必须要有一个初始值
    constructor(name: string, age: number) {
        // 在ts中需要在类中声明某个属性
        this.name = name
    }
    sayHi(msg: string): void {
        console.log(`I am ${this.name},${msg}`);
    }
}

访问修饰符作用: 控制类当中成员的可访问级别

class Student extends Person {
   private  constructor(name: string, age: number) {
        super(name, age)
        console.log(this.gender);

    }

    static create(name: string, age: number){
        return new Student(name, age)
    }
}

这里由于constructor被private修饰为私有的,所以不能通过new Student() 创建新对象的,只能通过在类的内部创建一个静态方法然后通过
Student.create("jack",18)创建对象
// protected 修饰的constructor也是不能被实例化的 相较于 private ,protected修饰的属性是允许被继承的

只读属性
readonly

class Person {
    public name: string  // ts中默认就是public
    private age: number = 18   //private是私有属性的,只能在类的内部访问
    protected readonly gender: boolean   // 只允许在子类中访问修饰的成员 只读
    constructor(name: string, age: number) {
        // 在ts中需要在类中声明某个属性
        this.name = name

        this.gender = true
    }
    sayHi(msg: string): void {
        console.log(`I am ${this.name},${msg}`);
    }
}
TypeScript的类与接口

接口中定义方法,只做对于方法的约束,而不做具体实现

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 Eat,Run{
    eat(food: string) {
    }
    run(distance: number) {
    }
}
TypeScript的抽象类 包含具体的实现
//定义抽象类,就是在class前面加上 abstract  ,被定义为抽象类之后,只能被继承,不能通过new 创建实例对象了
abstract class Animal {
    eat(food: string): void {
        console.log(`呼噜呼噜吃:${food}`);

    }
    abstract run(distance: number): void  // 抽象方法,不需要具体的方法体,不需要方法实现
}
class Dog extends Animal {
    run(distance: number): void {
        console.log('runrunrunrun');
    }
}
TypeScript的泛型

声明函数的时候不去指定类型,到调用的时候传递具体类型,目的是极大程度的复用代码

// 常规操作
function createNumberArray(length: number, value: number): number[] {
    const arr = Array<number>(length).fill(value)
    return arr
}
const res = createNumberArray(3, 100)
// 泛型这么用
function createArray<T>(length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}
const res1 = createArray<string>(3,'foo')    // 使用的时候定义类型
TypeScript的类型声明

当第三方npm 模块不一定是用ts写的,所以不一定有强类型的体验,比如安装lodash,这个时候 我们需要安装npm i @types/lodash --dev

import { camelCase } from 'lodash'
declare function camelCase(input: string): string 
const res  = camelCase("hello world")

总结:在ts中,我们需要引入第三方模块的时候,如果这个模块中不包含对应的类型声明文件,我们就要去尝试安装对应的类型声明模块,一般是@types/模块名,如果没有对应的模块的话,这个时候就要我们手动的使用declare语句去声明对应的模块类型

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。