TypeScript

369 阅读7分钟

从类型安全的角度可以分为强类型与弱类型

从类型检查的角度可以分为静态类型与动态类型

javaScript就是典型的弱类型且动态类型语言,语言本身的类型系统是非常薄弱的,几乎没有任何的类型限制,及其灵活多变的,缺失了类型系统的可靠性

强类型语言不允许有任意的隐式类型转换,而弱类型语言则允许任意的数据类型转换

JavaScript 弱类型产生的问题

1. 异常需要等到运行时才能发现

const obj = {}
setTimeout(() => {
  obj.foo()
}, 1000000)

2. 函数功能可能发生改变

function sum (a, b) {
  return a + b
}

console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // 100100

3. 对象索引器的错误用法

const obj = {}

obj[true] = 100 // 属性名会自动转换为字符串

console.log(obj['true'])

强类型的优势

1. 强类型代码错误更早暴露

2. 强类型代码更智能,编码更准确

3. 重构更可靠

4. 减少了代码层面的不必要的类型判断

静态类型与动态类型

静态类型:一个变量声明时它的类型就是明确的,声明过后,类型不允许再修改

动态类型:运行阶段才能够明确变量类型,变量类型可随时发生变化。变量是没有类型的,变量中存放的值是有类型的

Flow    javaScript 的类型检察器

// @flow    需要有这个标记才能检查到标注类型是否正确
// 安装flow  yarn add flow-bin --dev
function sum (a: number, b: number) { // 类型注解
  return a + b
}

sum(100, 100)

// sum('100', '100')

// sum('100', 100)

TypeScript

缺点一:语言本身多了很多概念,如接口、泛型、枚举,这些概念会增加学习成本,好在TypeScript属于渐进式,意思就是 即便我们什么类型都不知道,还是可以按照javascript语言的语法去编写代码,在了解过程当中学习到什么特性再使用

缺点二:项目初期会增加一些开发成本,因为在项目初期需要去编写很多类型声明

快速上手

安装,初始化项目

命令:yarn init --yes

          yarn add typescript --dev   这个模块提供了一个tsc的命令,可编译ts文件,同时会去检验类型异常

          yarn tsc '文件名'

配置文件

命令:yarn tsc --init    会生成一个tsconfig.json的文件

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    // "incremental": true,                   /* Enable incremental compilation */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    "outDir": "dist",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
    // "noUncheckedIndexedAccess": true,      /* Include 'undefined' in index signature results */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

常用属性说明target  // 编译后的语法outDir  // 编译后文件目录rootDir   // 源代码目录sourceMap  // 开启源代码映射,方便调试strict  // 开启严格模式,对于类型的检查会变得非常的严格

原始数据类型

// 原始数据类型
const a: string = 'foobar'

const b: number = 100 // NaN Infinity

const c: boolean = true // false

// 在非严格模式(strictNullChecks)下,
// string, number, boolean 都可以为空
// const d: string = null
// const d: number = null
// const d: boolean = null

const e: void = undefined

const f: null = null

const g: undefined = undefined

// Symbol 是 ES2015 标准中定义的成员,
// 使用它的前提是必须确保有对应的 ES2015 标准库引用
// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015
const h: symbol = Symbol()

// Promise

// const error: string = 100

作用域问题:在不同文件中定义了相同变量的问题

// 默认文件中的成员会作为全局成员
// 多个文件中有相同成员就会出现冲突
// const a = 123

// 解决办法1: IIFE 提供独立作用域
// (function () {
//   const a = 123
// })()

// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
// 模块有单独的作用域
const a = 123

export {}

Object类型  

范指非原始类型,如对象、数组、函数

// Object 类型

export {} // 确保跟其它示例没有成员冲突

// object 类型是指除了原始类型以外的其它类型
const foo: object = function () {} // [] // {}

// 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」
// 赋值的类型结构应与声明的类型结构完全一致,不能多 也不能少
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }

// 数组类型export {} // 确保跟其它示例没有成员冲突// 数组类型的两种表示方式const arr1: Array<number> = [1, 2, 3]const arr2: number[] = [1, 2, 3]// 案例 -----------------------// 如果是 JS,需要判断是不是每个成员都是数字// 使用 TS,类型有保障,不用添加类型判断function sum (...args: number[]) {  return args.reduce((prev, current) => prev + current, 0)}sum(1, 2, 3) // => 6

// 枚举(Enum)

export {} // 确保跟其它示例没有成员冲突

// 用对象模拟枚举
// const PostStatus = {
//   Draft: 0,
//   Unpublished: 1,
//   Published: 2
// }

// 标准的数字枚举
// enum PostStatus {
//   Draft = 0,
//   Unpublished = 1,
//   Published = 2
// }

// 数字枚举,枚举值自动基于前一个值自增
// enum PostStatus {
//   Draft = 6,
//   Unpublished, // => 7
//   Published // => 8
// }

// 字符串枚举
// enum PostStatus {
//   Draft = 'aaa',
//   Unpublished = 'bbb',
//   Published = 'ccc'
// }

// 常量枚举,不会侵入编译结果
const enum PostStatus {
  Draft,
  Unpublished,
  Published
}

const post = {
  title: 'Hello TypeScript',
  content: 'TypeScript is a typed superset of JavaScript.',
  status: PostStatus.Draft // 3 // 1 // 0
}

// PostStatus[0] // => Draft

// 函数类型
// 函数的输入、输出的类型需保持一致,输入的参数个数也需要保持一直
export {} // 确保跟其它示例没有成员冲突

function func1 (a: number, b: number = 10, ...rest: number[]): string {
  return 'func1'
}

func1(100, 200)

func1(100)

func1(100, 200, 300)

// 任意类型(弱类型)
// 语法上都不会报错
export {} // 确保跟其它示例没有成员冲突

function stringify (value: any) {
  return JSON.stringify(value)
}

stringify('string')

stringify(100)

stringify(true)

let foo: any = 'string'

foo = 100

foo.bar()

// any 类型是不安全的

// 隐式类型推断
export {} // 确保跟其它示例没有成员冲突

let age = 18 // number

// age = 'string'

let foo // 此时foo为any类型

foo = 100

foo = 'string'

// 建议为每个变量添加明确的类型标注

// 类型断言

export {} // 确保跟其它示例没有成员冲突

// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]

const res = nums.find(i => i > 0)  // 此时res推断类型为number | undefield

// const square = res * res

const num1 = res as number  // 断言,此处明确res为number类型

const num2 = <number>res // JSX 下不能使用