大纲
本章主要是一些ts的类型工具以及模块化和相关实践,涉及以下内容:
- 类型编程
- module和namespace
- 小技巧
- ts在react中的实践
这篇稍微有点零碎,建议多查阅文档或者查阅相关的文章进行更多的了解,代码示例以告知 QAQ
类型编程
- 索引类型(假定我们要取出如下对象的name属性对应的值)
const user = { name: 'lili', age: 20, sex: 'woman', } // 声明类型 interface User { [key: string]: any } function choose(obj: User, key: string[]): any[] { return key.map(item => obj[item]) // 返回对应属性的值 返回的是数组 } choose(user, ['name']) 复制代码
- 索引类型查询操作符
class Person1 { public name: string = 'lili' public age: number = 20 } type getTypePerson = keyof Person1 interface Point { x: number; y: number; } // type keys = "x" | "y" type keys = keyof Point; 复制代码
- 索引访问操作符 T[K][], 之前也提到过
function Person2<T, K extends keyof T>(obj: T, name: K[]): T[K][] { return name.map(item => obj[item]) } const User2 = { name: 'lili', age: 20} Person2(User2, ['name', 'age']) // 以下是代码提示: (相对更加严谨) // (local function) Person2<{ // name: string; // age: number; // }, "name" | "age">(obj: { // name: string; // age: number; // }, name: ("name" | "age")[]): (string | number)[] 复制代码
- 映射类型 [K in keyof T]
interface User3interface { name: string, age: number, } // 添加每个都为可选类型 type User3<T> = { [K in keyof T]?: T[K] } type User3s = User3<User3interface> // 代码提示: // type User3s = { // name?: string | undefined; // age?: number | undefined; // } 复制代码
- 截获函数返回值类型 ---- ReturnType
interface User4Interface { name: string, age: number, } type UserType = () => User4Interface type User4 = ReturnType<UserType> // User4Interface 复制代码
- 类型递归
interface Other { hobby: string, sex: string, } interface User5 { name: string, age: number, other: Other } // 为每个类型添加一个可选类型 // 1. 类型工具 Partial type U1 = Partial<User5> // 代码提示: // type U1 = { // name?: string | undefined; // age?: number | undefined; // other?: Other | undefined; // } // 2. 手动实现 type Particals<T> = { [U in keyof T]?: T[U] extends object ? Particals<T[U]> : T[U] } type U2 = Particals<User5> // type U2 = { // name?: string | undefined; // age?: number | undefined; // other?: Particals<Other> | undefined; // } 复制代码
- 工具类型 ’+‘号 ’-‘号 这两个关键字用于映射类型中给属性添加修饰符,比如-?就代表将可选属性变为必选,-readonly代表将只读属性变为非只读。比如TS就内置了一个类型工具Required,它的作用是将传入的属性变为必选项:
- Required 将传入的属性变为必选项
type Required<T> = { [P in keyof T]-?: T[P] }; 复制代码
- Exclude 的作用是从 T 中排除出可分配给 U的元素. T extends U指T是否可分配给U
type Exclude<T, U> = T extends U ? never : T; type T = Exclude<1 | 2, 1 | 3> // -> 2 复制代码
- Omit
type Omit<T, K> = Pick<T, Exclude<keyof T, K>> type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number } 复制代码
- Merge Merge的作用是将两个对象的属性合并:
// type Merge<Obj1, obj2> = Compute<A> + Omit<U, T> // type obj3 = Merge<obj1, obj2> type obj1 = { name: string, age: number } type obj2 = { sex: string, hobby?: string, } type Compute<A extends any> = A extends Function ? A : { [K in keyof A]: A[K] } type obj3 = Compute<obj1 & obj2> // type obj3 = { // name: string; // age: number; // sex: string; // hobby?: string | undefined; // } 复制代码
- Intersection Intersection是Extract与Pick的结合,Intersection = Extract + Pick
type obj4 = { name: string, age: number, } type obj5 = { name: string, sex: string, } type Intersection<T extends object, U extends object> = Pick<T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T>> // Extract 提取 type obj6 = Intersection<obj4, obj5> // type obj6 = { // name: string; // } 复制代码
- Overwrite顾名思义,是用U的属性覆盖T的相同属性.
type obj7 = { name: string, age: number, } type obj8 = { name: number, } // import { Diff } from 'utility-types' 要安装一下库 type Overwrite<T extends object, U extends object, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I> type obj9 = Overwrite<obj7, obj8> // type obj9 = { // name: number; --- 被重写了 // age: number; // } // Mutable 将 T 的所有属性的 readonly 移除 type Mutable<T> = { -readonly [P in keyof T]: T[P] } type ob = { readonly name: string, age: number, } type obj10 = Mutable<ob> // type obj10 = { // name: string; // age: number; // } 复制代码
module和namespace
- 全局变量 比如在1.ts定义:const a = 1, 2.ts定义: const a = 2 这样全局中定义的变量会报错 因为重复声明了 避免全局变量
- 导出类型
export type User = { name: string, age: number, } 复制代码
- 导出多个类型
// 导出多个类型 type User1 = { name: string, age: number, } interface User2 { name: string, age: number, } // "isolatedModules": false, tsconfig.json 要设置一下 export { User1, User2 } 复制代码
- 重命名
export { User1 as user1 } 复制代码
- 引入
import { User2 as user2 } from './module'; // 整体导入 import * as u from './module'; // 同样的 --- 默认导出 export default () => { } 复制代码
- namespace 其实一个命名空间本质上一个对象,它的作用是将一系列相关的全局变量组织到一个对象的属性 如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 /// 引用它,语法格式如下:
// index.ts // 声明全局的明明空间 declare namespace USER { // 导出接口 export interface find { (name: string): string } export interface create { (name: string): string } export interface update { (name: string): string } export interface deleted { (name: string): string } } // 引入文件 /// <reference path = "index.ts" /> // 使用命名空间的模块 interface API { USER: { update: USER.update // ..... }, } 复制代码
小技巧
- 注释,利于智能提示快速定位代码
interface User { /** * 用户 */ name: string, age: number, } const user: User = { name: 'lili', age: 20 } // 以下是代码提示 // (property) User.name: string // 用户 复制代码
- 类型推导
function getUser(name: string): string { return name } // typeof 获取整体函数的类型 type getU = typeof getUser // type getU = (name: string) => string // h获取返回值的类型 type returnT = ReturnType<typeof getUser> // type returnT = string 复制代码
- 巧用Omit 有时候我们需要复用一个类型,但是又不需要此类型内的全部属性,因此需要剔除某些属性,这个时候 Omit 就派上用场了。
interface User1 { name: string, age: number, sex: string, } type newUser1 = Omit<User1, 'sex'> // type newUser1 = { // name: string; // age: number; // } 复制代码
- Record 高级类型 Record 允许从 Union 类型中创建新类型,Union 类型中的值用作新类型的属性。有利于类型安全
type List = 'u1' | 'u2' | 'u3' // 要求用户表的属性必须包含 { name: string, age: number } 类型字段 type UserList = Record<List, { name: string, age: number }> const userList: UserList = { u1: { name: 'lili', age: 20 }, u2: { name: 'xiaoming', age: 21 }, u3: { name: 'xiaohong', age: 22 }, } 复制代码
react实践
- 编写一个无状态的组件
写法1:
// 定义一个接口 interface Rrop { /** * 这是一个react组件的属性 */ name: string, age: number, } // 无状态组件 export const comp: React.SFC<Rrop> = props => { const { name, age } = props return ( <div>name: {name} age: {age}</div> ) } 复制代码
写法2:
type Props1 = { click(e: React.MouseEvent<HTMLElement>): void children?: React.ReactNode } const handleClick = () => console.log('click') const Events = ({ click: handleClick}: Props1) => { <div onClick={handleClick}></div> } 复制代码
- 编写状态组件 避免状态被显示改变 所以 需要赋 readonly
const initialState = { name: 'lili', age: 20 } type States = Readonly<typeof initialState> export class InitialComp extends React.Component { // 再次只读 public readonly state: States = initialState render() { return ( <div>hello</div> ) } } 复制代码
- ref
interface Props { name: string, age: number, } interface State { name: string, } export class StateComp extends React.Component<Props, State> { private inputRef = React.createRef<HTMLInputElement>() // 创建ref div 组件的话那么这个类型就是 HTMLDivElement。 constructor(props: Props) { super(props) this.state = { // (property) React.Component<Props, State, any>.state: Readonly<State> 组件自动为我们分配的 name: 'lili' } } private setStat(val: string) { this.setState({ name: val }) } // onChange事件 private onChangeInput(e: React.ChangeEvent<HTMLInputElement>) { this.setState({ name: e.target.value }) } // form表单事件 private handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault() } render() { return ( <> <div>{this.state.name}</div> <input ref={this.inputRef} onChange={e => this.onChangeInput(e)}/> </> ) } } 复制代码
- 默认属性传递
interface Other { hobby? : string } class ClassProp { public name: string = '' public submit = (name: string): string => name public other: Other = { hobby: '' } } // 实例化类传递 默认属性 export class PropsClass extends React.Component<ClassProp, State> { // 1. 可以减少代码量 // 2. 减少出错率 public static defaultProps = new ClassProp() public render() { const { name, other } = this.props // 如果遇到属性找不到可以三木运算符判断下或者是 other!.hobby return ( <div>{ name } { other.hobby}</div> ) } } 复制代码
- promise类型问题
// promise类型问题 interface propP<T> { name: string, age: number, classes: T, } export class PromiseComp extends React.Component { // 返回promise对象类型 public async getData(name: string): Promise<propP<string[]>> { // 表示返回的res的参数类型 return { name: 'lili', age: 20, classes: ['class1','clas2'], } } public request() { this.getData('lili').then(res => { console.log(res) }) } } 复制代码
到此,系列文章基本完结啦~ 近期也在整理一些 ts 实践性的文章,主要以react框架为主,过段时间会陆续上传,如果对大家有帮助记得点个赞~ , 如有错误请指正, 我们一起解决,一起进步。