TypeScript 进阶技巧
IES 前端架构 - 基础体验 - 龙逸楠
- TypeScript 概览
- 项目配置
- 类型使用技巧
- 旧项目如何渐进式迁移
TypeScript 概览
- 典型特征
- Superset of JavaScript
- Optional types
- No runtime overhead
- Toolchains
Toolchains
IntelliSense
Toolchains
Hover information
Toolchains
Hover information
Toolchains
Rename
Toolchains
-
Code navigation
-
Refactoring
-
Auto imports
-
Code suggestions
-
....
TypeScript 项目配置
- 前端项目
- NodeJS 项目
前端项目配置
{
"compilerOptions": {
"strict": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"target": "ES5",
"module": "ESNext",
"importHelpers": true,
"jsx": "react",
"moduleResolution": "Node",
"baseUrl": "./src",
"paths": {
"~/*": [
"./*"
]
}
},
"include": ["./src"],
"exclude": ["node_modules"]
}
Webpack
- Webpack alias
- Webpack loader
- Polyfill
Webpack alias
TypeScript 不处理 import 的路径,baseURL + paths 只影响 type resolution,不影响 emit 出来的代码。
- https://github.com/dividab/tsconfig-paths-webpack-plugin
- 手动 alias
Webpack loader
You may don't need babel/babel-loader
- 没有使用稳定阶段小于 Stage3 的语法
- 使用的第三方库里面没有任何依赖特定 babel plugin
- Polyfill 很复杂,手动管理成本太高
TypeScript 也可以用来编译 JavaScript,只需要加上 allowJs 编译参数
只使用 TypeScript 编译的好处
- 更快的编译速度
- 更小的编译产物
- 更好的运行时性能
https://github.com/ReactiveX/rxjs/pull/2093
Polyfill
需要手动配置
通过 core-js 可以配置出简单通用并且体积可控的 Polyfill
import 'core-js/features/promise'
import 'core-js/features/array'
import 'core-js/features/object'
import 'core-js/stage/3'
Async polyfill
const Polyfills: Promise<void>[] = []
export function asyncPolyfill() {
if (typeof Promise === 'undefined') {
// @ts-ignore
Polyfills.push(import('core-js/features/promise'))
}
if (!Object.assign) {
// @ts-ignore
Polyfills.push(import('core-js/features/object/assign'))
}
return Promise.all(Polyfills)
}
import { render } from 'react-dom'
import { asyncPolyfill } from './async-polyfill'
asyncPolyfill().then(() => {
render(...)
})
NodeJS 项目配置
{
"compilerOptions": {
"strict": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"target": "ES2018",
"module": "CommonJS",
"importHelpers": true,
"moduleResolution": "Node",
"baseUrl": "./src",
"paths": {
"~/*": [
"./*"
]
}
},
"include": ["./src"],
"exclude": ["node_modules"]
}
Development tools
- ts-node
- nodemon
- module-alias
nodemon + ts-node + tsconfig-paths
Development toolchains
{
"delay": 1000,
"restartable": "rs",
"env": {
"NODE_ENV": "development"
},
"execMap": {
"ts": "ts-node -r tsconfig-paths/register"
// node --inspect-brk -r ts-node/register -r tsconfig-paths/register
},
"watch": [
"src/**/*.ts"
]
}
nodemon ./src/index.ts
module-alias
Production setup
const { join } = require('path')
const moduleAlias = require('module-alias')
moduleAlias.addAlias('~', join(__dirname, 'lib'))
moduleAlias()
require('./lib')
TypeScript 类型使用技巧
Nullable type + Type Guards
import * as React from 'react'
export interface User {
_id: string
name: string
avatarUrl?: string
}
const findUserById = (id: string): User | null => {
// ...
return null
}
const props = {
router: {
params: {
id: ''
}
}
}
const user = findUserById(props.router.params.id)
const name = (<div>{user.name}</div>) // error
TypeScript 类型使用技巧
Union type + Type Guards
import * as React from 'react'
export interface User {
_id: string
name: string
avatarUrl?: string
}
type UserInfo = User | string
const findUserById = (id: string): User | null => {
// ...
return null
}
declare const userInfo: UserInfo
let user: User | null = null
if (typeof userInfo === 'string') {
user = findUserById('')
} else {
user = userInfo
}
const name = (<div>{user?.name}</div>)
TypeScript 类型使用技巧
Literal type
interface User {}
type UserType = 'admin' | 'customer' | 'guest'
export function findUserByType(userType: UserType): User[] {
return []
}
findUserByType('admin')
TypeScript 类型使用技巧
Literal type
interface User {}
type UserType = 'admin' | 'customer' | 'guest'
export function findUserByType(userType: UserType): User[] {
return []
}
findUserByType('admin')
TypeScript 类型使用技巧
keyof 运算符
interface User {
_id: string
name: string
avatarUrl: string
age: number
birthDay: Date
}
function checkPropertyValidate(user: User, key: keyof User) {
}
declare const user: User
checkPropertyValidate(user, '_id')
TypeScript 类型使用技巧
mapped type
interface User {
_id: string
name: string
avatarUrl: string
age: number
birthDay: Date
}
type AsyncUser = {
[K in keyof User]: Promise<User[K]>
}
function fillUserAsync(user: User): AsyncUser {
return Object.create({})
}
declare const user: User
fillUserAsync(user).age
TypeScript 类型使用技巧
函数重载
interface UseUserIDConfig {
useId: true
// ....
}
interface UseUserEntityConfig {
useId: false
// ...
}
function getUser(config: UseUserIDConfig, id: string): void;
function getUser(config: UseUserEntityConfig): void;
function getUser(config: UseUserIDConfig | UseUserEntityConfig, id?: string) {
}
getUser({ useId: true })
https://www.typescriptlang.org/docs/handbook/advanced-types.html
旧项目如何渐进式迁移
https://github.com/Brooooooklyn/typescript-example-projects/tree/master/javascript-migrate-to-typescript
Q & A
TypeScript 进阶技巧
By yinan
TypeScript 进阶技巧
- 1,112