2021-07-15 系统化装修 2021-03-15 学习TS handBook V2 系统回顾 2019-12-11 初稿 /frontEndBasic/typescript
0 存在的意义
对前端最大的意义是:重塑类型思维。时刻把我类型进行代码约束。
官方文档这样介绍:
TypeScript 是一个开源的编程语言,通过在 JavaScript(世界上最常用的语言之一) 的基础上添加静态类型定义构建而成。 类型提供了一种描述对象形状的方法。可以帮助提供更好的文档,还可以让 TypeScript 验证你的代码可以正常工作。
在 TypeScript 中,不是每个地方都需要标注类型,因为类型推断允许您无需编写额外的代码即可获得大量功能。
1 安装、配置、使用
专门搞了一个仓库 https://gitee.com/xiaoxfa/review-ts
touch index.ts
npx tsc --init # 生成tsconfig.json 具体看最下面章节
npx tsc src/1.ts
多种方式:
- tsc
tsc HelloWorld.ts --strict --alwaysStrict false --watch
ts-node a.js
全局依赖node --experimental-loader esbuild-node-loader file.ts
用到了一个基于 esbuild 的loader,推荐下面的简单npx esno a.ts
如果是 node CJS 模块npx esmo a.ts
如果是 esm 并且type:module
- 使用babel
2 ts 入门
基本类型
ts可以指定和推断基本类型,这个比较简单,放几个代码片段做说明。
需要强调的是,对于基本类型,实际中不需要声明,直接推断即可。
// number string boolean
const num: number = 5
const name1: string = "xinbao"
// Array 两种写法
const list1: number[] = [1, 2, 3] // 常规
const list2: Array<number> = [1, 2, 3] // 数组泛型
const list3: Array<number | string> = [1, 2, 3, "4"]
// tuple 元组 就是有要求的数组,定义了内部的结构类型
let x: [string, number]
// object
let obj1: { id: number; y: number } = { id: 1, y: 2 }
// any
// void
// Null Undefined
const u: undefined = undefined;
const n: null = null;
// never 永不存在的值类型。返回值是抛出异常,不会有返回值的函数表达式,箭头函数的返回值类型
// 类型断言 type assertions 明确知道结构
// let slen: number = (<string>sV).length;
let slen: number = (sV as stirng).length;
枚举 Enum
把一组数值 变为 有意义的名字。用途:抽离魔法常量,单独维护,节省记忆成本
enum Color {
Red = 1,
Green = 2,
Blue = 4
}
let c: Color = Color.Red // 1
// 也可以反查
let name:string = Color[2]
name // Green
Interface & Type
两者都是实现对对象的类型定义,不会参与编译,再开发阶段起辅助作用。
Interface
interface Data {
name: string;
age?: number; // 可选属性
[x: string]: any; // 字符串索引签名
readonly id: number; // 只读
}
// 场景:对参数进行约束
function fun(persion: Data){
return persion.name
}
Type
可以约束简单值,也可以约束其他类型。一定程度上可以混用。
type Label = 'a'|'b';
type Add = (x: number, y: number) => number
let add: Add = (a, b) => a + b
// 混合接口
interface Lib {
label:Label;
version: string;
dost(): void;
}
小技巧:遍历 Interface 里的 key 值 ,得到属性集合。具体详见下面工具方法部分。
interface Person{ a:string; b:number}
type K1 = keyof Person; // 'a'|'b'
interface 和 type 的关系
interface 和 type :
- interface 和 type 都可以定义类型,通用
- interface extends , interface A & B 和 type & typeB 效果一致
不同之处:
Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable. Type和 Interface 非常相似,很多时候可以自由选择。interface的大多数特性type也有,不同之处在于: type 不能被重新补充新的属性,而interface可以extends拓展新属性。
这里是指type本身不能重新覆盖、新增,如果是另起一个新类型不会有问题。见 Demo 。
函数
就函数重载说一下。
// 函数重载
function add8(...rest: number[]): number
function add8(...rest: string[]): string
function add8(...rest: any[]): any {
let first = rest[0]
if (typeof first === "string") {
return rest.join("")
}
if (typeof first === "number") {
return
}
}
类
也包含了 继承 抽象 接口 setter getter等。这里内容其实比较多,本次先忽略了。
抽象类 多态
泛型 generics
实现一个打印函数,传入啥就打印啥。
// 可以使用联合类型
function log_a(value: string | string[]): string | string[] {
return value
}
// 或者 any 搞定,但这样丢失了一些信息,不知道约束关系
function log_b(value: any):any {
return value
}
// 如果使用泛型就简单了,传入T 返回T
function log<T>(value: T): T {
return value
}
log<string[]>(["1"])
类型推断
会自动进行推断,从右到左。
上下文类型推断。还可以 as,还可以 <>
。
基本概念就都结束了。
3 tsconfig.json
这里请忽略,直接跳过!
详情 https://www.staging-typescript.org/tsconfig
本身是一个json,拉钩上的文章解析:其他人的解析
类型 | 值 | 描述 | 备注 |
---|---|---|---|
compilerOptions编译选项 | |||
基础选项 | |||
incremental | true/default | 增量编译 | |
target | es5/default | 编译成啥版本 | es3/es5/es2015/es2016/…/esnext |
module | commonjs | 代码生成格式 | none/commonjs/amd/system/umd/es2015/es2020/esnext |
lib | [] | 指定编译包含的类库文件 | |
allowJs | true | 是否允许js参与编译 | |
checkJs | true | 是否检查js报错 | |
jsx | preserve 保持现状 | 设置jsx代码生成 | preserve/react-natice/react/react-jsx/react-jsxdev |
declaration | true | 生成对应的 .d.ts 文件 | |
declarationMap | true | 生成 .d.ts 的 sourcemap文件 | |
sourceMap | true | 生成 souecemap | |
outFile | ./ | 输出文件 | |
outDir | ./ | 输出目录 | |
rootDir | ./ | 设定根目录 | |
composite | true | 启用项目编译 | |
tsBuildInfoFile | ./ | 设定文件保存增量编译信息 | |
removeComments | true | 移除注释 | |
noEmit | true | 不输出 | 不生成文件,只检查vue-tsc |
importHelpers | true | 从 tslib 引入helper | |
downlevelIteration | true | 在早期版本中提供对迭代/展开/解构的支持 | es3/es5 |
isolatedModules | true | 传输每一个文件作为一个分离的模块 | 类似ts.transpileModule |
严格类型检查选项 | |||
strict | true | 启用全部严格类型检查选项 | 一定打开 |
noImplicitAny | true | 禁止隐式 any 类型 | 一般做不到,看权衡 |
strictNullChecks | true | 严格null检查 | |
strictFunctionTypes | true | 检查函数类型 | |
strictBindCallApply | true | 更严格地检查 call、bind、apply 函数的调用 | 不太理解 |
strictPropertyInitialization | true | 类的初始化属性严格检查 | |
noImplicitThis | true | 禁止隐式 this 类型 | |
alwaysStrict | true | 每个文件添加 use strict 启用严格模式 | |
额外的检查 | |||
noUnusedLocals | true | 未使用的本地数据报错 | |
noUnusedParameters | true | 未使用的参数报错 | |
noImplicitReturns | true | 禁止隐式返回。如果代码的逻辑分支中有返回,则所有的逻辑分支都应该有返回。 | |
noFallthroughCasesInSwitch | true | switch 要有失败分支 | |
noUncheckedIndexedAccess | true | 在索引签名结果中包含 undefined | |
noImplicitOverride | true | Ensure overriding members in derived classes are marked with an ‘override’ modifier. | |
noPropertyAccessFromIndexSignature | true | Require undeclared properties from index signatures to use element accesses. | |
模块解析选项 | |||
moduleResolution | node | 模块解析策略 | 一般选node |
bseUrl | ./ | 解析非绝对路径模块名时的基准目录 | |
paths | {} | 可理解为 webpack 的 alias 别名配置 | “@src/“: [“src/“], |
rootDirs | [] | 指定多个目录作为根目录 | |
typeRoots | [] | 指定类型文件的根目录 | |
types | [] | 默认情况下,所有的 typeRoots 包都将被包含在编译过程中 | |
allowSyntheticDefaultImports | true | 允许合成默认导出 | dayjs? |
esModuleInterop | true | Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies ‘allowSyntheticDefaultImports’. | 和上面同时开启 |
preserveSymlinks | true | 不解析symlinks真实路径 | |
allowUmdGlobalAccess | true | 允许从模块中访问umd全局变量 | |
sourceMap选项 | |||
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. | |
实验性选项 | |||
experimentalDecorators | true | 开启es7的decorators装饰器 | 用了装饰器一定开这个 |
emitDecoratorMetadata | true | 支持 metadata | |
高级选项 | |||
skipLibCheck | true | 跳过检查声明文件 | |
forceConsistentCasingInFileNames | true | 不允许对同一文件的大小写不一致的引用 |
除了 compilerOptions,还有include/exclude/files/extends
一般不用管。
4 工具类
使用场景有很多,比如在项目中需要输入框、异步请求、数据展示,对于其中的很多字段是重复出现的,如果刻意联系,可以重新组织这些对应的类型。如果一个字段变了,相关联的字段会报错,一次改动,多处报错,依次解决就可以。
实际来看,如果不是代码洁癖,可以不使用这些。有些面试还会问常用工具类的底层实现,这个见仁见智吧。
keyof
可以通过 keyof
获得一个类型里的 propName,刚才提过。
interface Point {
a1:string;
a2:number;
}
type R= keyof Point
const r:R='a1'
方便约束传参。
element-plus
import { ElForm } from 'element-plus'
// 这时候 ElForm 是一个对象
typeof
js里有,ts里有特殊含义,可以约束类型.typeof 对象
,获取对象表示的类型。
let s = "hello";
let n: typeof s = 'fff' // 动态约定
// 简单的config可以不抽取类型
const baseInfo={name:'',age:1}
const me:typeof baseInfo={
name:'',
age:2
}
还有一个常见的用法
type Info = keyof typeof me // 先转成类型,再读取类型的key // name age
ReturnType
函数返回值
function f() {
return { x: 10, y: 3 };
}
// 可以获得返回值的类型
type P = ReturnType<typeof f>; // 两者一致
type R = {
x: number;
y: number;
}
还有有很多内置的工具类,简单一提:
Partial<Type>
把type里的类型变为可选Required<Type>
反过来,都是必选Readonly<Type>
设定只读Record<Keys, Type>
,映射场景vue 路由
interface Conf{
name:string;
path:string;
}
type Keys = 'index'|'list'
const f:Recrod<Keys,Conf>={
index:{name:'',path:''},
list:{name:'',path:''}
}
Pick<Type,Keys>
从一个大的type里挑几个类型,组成新的Omit<Type, Keys>
从一个大的类型里删几个类型,组成新的Exclude<Type, ExcludedUnion>
给多个类型,取不重复的type T1 = Exclude<"a" | "b" | "c", "a" | "b">
得到'c'
Extract<Type, Union>
给多个类型,取重复的类型NonNullable<Type>
去掉null
和undefined
干净的类型Parameters<函数>
看函数的入参类型,本身是个数组InstanceType<T>
获取构造函数类,实例类型组成的类型
声明文件
jquery是amd类库。jquery不支持ts,需要声明文件。
npm i @types/jquery -D
DefinitelyTyped 是最大的公共存储库,包括1000多个库文件。也有一个用来管理 TypeScript 定义的 Node.js 流行模块,叫 Typings。
如何写声明文件。
*.d.ts
declare function globalLib(options:flobalLb.Options):viod
实战
- 最佳实践
- 使用场景
- vue实战
- vue2.x + ts 经验小结
- 系统中的ts和lint vue2.x+ts
- Node实战
公司用了 swagger 想自动生成 ts 类型改怎么做?
目前有两个技术选型,自己看着来
- alibaba/pont
- swagger-ts-api https://www.npmjs.com/package/swagger-typescript-api
也可考虑: pont-engine
- 如何关注最新进展
参考链接
- 参考资料
- 官网文档
- 拉钩教育
当你需要对js运行时进行检查,可以参考这篇文章 TypeScript 运行时类型检查指南,这是一篇翻译文章,暂时用不到。
写在最后
那几年成长真的好慢,很早就尝试过ts,但是搞不懂工程化,白蹉跎了那段好时光。