typescript修炼指南(四)

作者: aliensq | 来源:发表于2019-12-31 15:06 被阅读0次

    大纲

    本章主要是一些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<T>,它的作用是将传入的属性变为必选项:
    1. Required 将传入的属性变为必选项
    type Required<T> = { [P in keyof T]-?: T[P] };
    
    1. 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
    
    1. Omit
    type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
    
    type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }
    
    1. Merge Merge<O1, O2>的作用是将两个对象的属性合并:
    // 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;
    // }
    
    1. Intersection Intersection是Extract与Pick的结合,Intersection<T, U> = Extract<T, U> + Pick<T, U>
    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;
    // }
    
    1. Overwrite<T, U>顾名思义,是用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框架为主,过段时间会陆续上传,如果对大家有帮助记得点个赞~ , 如有错误请指正, 我们一起解决,一起进步。

    相关文章

      网友评论

        本文标题:typescript修炼指南(四)

        本文链接:https://www.haomeiwen.com/subject/thgboctx.html