typescript修炼指南(四)

大纲

本章主要是一些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框架为主,过段时间会陆续上传,如果对大家有帮助记得点个赞~ , 如有错误请指正, 我们一起解决,一起进步。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容