Blog Logo

electron+hooks+ts实现互动直播大班课(五)

写于2020-08-28 04:05 阅读耗时10分钟 阅读量


这是该系列最后一篇文章,核心讲解TypeScript

ts

内容如下:

  • 什么是TypeScript?
  • 为什么使用TypeScript?
  • 可以不使用TypeScrip?
  • TypeScript优势
  • TypeScript劣势
  • TypeScript基础知识
    • 给变量加约束
      • 原始类型
      • 联合类型
      • 任意类型
      • 枚举类型
      • 对象类型
      • 数组类型
    • 给函数加约束
      • 给函数加约束
      • 给类加约束
      • 类型断言作用
      • 类型断言 vs 类型转换
      • 类型断言 vs 泛型
  • 举例

1.什么是TypeScript?

TypeSciprt是JavaScript的超集,简单理解就是加了约束的JavaScript

JavaScript可以在哪些地方加约束呢? 无非就两个地方:变量函数也是函数)


2.为什么使用TypeScript?

加了约束后的JavaScript,可读性有很大的提升,将弱类型的语言变成了强类型的语言,当然就包括强类型的所有优点。


3.可以不使用TypeScript?

当然可以,只是目前越来越多的开发使用ts去写各种类库,想看其实现,学习ts是不错的选择。 其次,公司需要持续维护的前端项目,ts可以提高代码可读性,让陆陆续续离职入职的前端小伙伴们快速熟悉项目,交接接手更方便。


4.TypeScript优势

1.写通用组件类库 2.解读开源第三方ts库 3.拥于持续维护的前端项目


5.TypeScript劣势

优势说完,再来说说ts的劣势。 首先,ts需要一定的学习成本,得花时间和实践去理解其语法。 然后,ts导致项目代码量增大,编译会稍慢点,因为最后的构建部分ts最终还是会编译成js。 接着,ts比其他js项目开发周期会拉长,毕竟每个类每个函数每个参数都需要自定义。 最后,ts规范很重要,对于新手而言,如果项目参数全部用any声明,那还不如直接用js呢,切记不能滥用any。

总结一下: 1.有学习成本 2.代码增多,编译变慢、开发周期变长 3.ts如果使用不规范,还不如用js实在


6.TypeScript基础知识

1.给变量加约束

  • 原始类型
  • 联合类型
  • 任意类型
  • 枚举类型
  • 对象类型
  • 数组类型

1.原始类型:

//只允许赋值该类型(单个)
let str: string = 'hello';
let num: number = 1;
let bol: boolean = true;
let nul: null = null;
let un: undefined = undefined;

2.联合类型:

//只允许赋值该类型(多个)
let muchtype:string|number = '1';
muchtype = 2;

3.任意类型:

//允许赋值任意类型
//方式一:
let an: any = 'test';
//方式二:
let anyThing;

4.枚举类型:

enum ChatCmdType {
    init = 0, // 课程初始化
    chat = 1, // 群聊消息
    addAnnounce = 2, // 发布公告
    deleteAnnounce = 3, // 删除公告
    startCourse = 4, // 开始上课
    endCourse = 5, // 结束上课
}

5.对象类型: 用interface声明:

interface ChatMessage {
    account: string
    headImg?: string
    local: boolean
    readonly role: number
    text: string
    [k: string]: any
}

type声明:

type ChatMessage = {
    account: string
    headImg?: string
    local: boolean
    readonly role: number
    text: string
    [k: string]: any
}

解释下声明的对象类型:

const message: ChatMessage = {
    account: 'Tony',
    local: true,
    role: 1,
    text: 'Hello, Tim'
}
message.role = 2 // 报错
message.link = 'http://baidu.com'

声明一个聊天对象: 1.除了头像,其他都必填 2.角色是数字类型且只读 3.可以自定义其他属性


6.数组类型:

// 定义数组每个item的类型
let arr: number[] = [1, 2, 3];
let str: string[] = ['1', '2', '3'];
let an: any[] = ['1', 2, true];
let messages: ChatMessage = [{xxx}, {xxx}, ...]

2.给函数加约束

1.给函数加约束 给函数加类型,简单理解就是给函数的参数声明类型。

const on = (name: string, cb: (...args: any[]) => void) => {
    cb(true, name, 1)
}

声明一个on函数: 1.第一个参数string 2.第二个参数回调callback函数 3.callback函数的参数类型是多个的任意类型

on('Tony', (bool, name, age) => {
    console.log(bool, name, age)
})
// 输出 true Tony 1

注意cb: (...args: any[]) => voidcb: (args: any[]) => void的区别


2.给类加约束 严格意义上讲JavaScript是没有类的,只是在ES6时引入的一个语法糖class。 新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 函数是面向过程的写法,类是面向对象的写法。

class Person {
  private name: string;
  protected age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  public wark() {
    console.log(this.name);
  }
}

3.类型断言作用 1.将一个联合类型断言为其中一个类型

const muchtype:string|number = 1;
(muchtype as number).toString()

const muchtype:string|number = '1';
(muchtype as string).length

2.将任何一个类型断言为 any

const ipc = window.ipc;

会报错:Property 'ipc' does not exist on type 'Window & typeof globalThis'. TS2339。 方案一,as:

const ipc = (window as any).ipc;

方案二,加忽略注释:

// @ts-ignore
 const ipc = window.ipc;

4.类型断言 vs 类型转换 类型断言只会影响 TypeScript 编译时的类型,类型断言语句在编译结果中会被删除:

function toBoolean(something: any): boolean {
    return something as boolean;
}

toBoolean(1);
// 返回值为 1

在上面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用,代码在编译后会变成:

function toBoolean(something) {
    return something;
}

toBoolean(1);
// 返回值为 1

所以类型断言不是类型转换,它不会真的影响到变量的类型。 若要进行类型转换,需要直接调用类型转换的方法:

function toBoolean(something: any): boolean {
    return Boolean(something);
}

toBoolean(1);
// 返回值为 true

5.类型断言 vs 泛型 举个例子:

function getCacheData(key: string): any {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData('tom') as Cat;
tom.run();

我们使用泛型:

function getCacheData<T>(key: string): T {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData<Cat>('tom');
tom.run();

通过给 getCacheData 函数添加了一个泛型 ,我们可以更加规范的实现对 getCacheData 返回值的约束,这也同时去除掉了代码中的 any,是最优的一个解决方案。


7.举例

最后看一个完整案例: 一个简单的react组件:

import React from 'react';
interface ChildProps {
  onClick: (evt: any) => void
}
const Child: React.FC<ChildProps> = ({ onClick }) => {
  return (<span style={{ cursor: 'pointer' }} onClick={onClick}>Child</span>)
}
export default Child

一个复杂的react组件:

import React, { useEffect, useRef, useCallback } from 'react';
import logo from 'logo.svg';
import { connect } from 'dva';
import { FunCompProps } from 'utils/types'
import Child from './Child';
import Child2 from './Child2';
import Context from 'hooks/useContext'
import useTitle from 'hooks/useTitle'

type Login = {
  current: number
  record: number
}
type Loading = {
  global: boolean
  models: Object
  effects: { [k: string]: boolean }
}
interface LoginProps extends Login, FunCompProps {
  loading: boolean
}
type LoginModel = {
  login: Login
  loading: Loading
}

const Login: React.FC<LoginProps> = ({ dispatch, loading, current }) => {
  const preProps = useRef<number>()
  const inputElement = useRef<HTMLInputElement>(null)
  const lock = useRef<boolean>(false)

  useTitle('Login')
  useEffect(() => {
    preProps.current = current
  }, [current])

  const change = async (type: string) => {
    if (lock.current) return
    const delay = (timeout: number) => new Promise((resolve) => {
      setTimeout(resolve, timeout);
    })
    lock.current = true
    await delay(2000)
    await dispatch({
      type: `login/${type}`
    })
    lock.current = false
  }

  const onClick = useCallback(() => {
    inputElement.current?.focus()
  }, [])
  return (
    <Context.Provider value={current}>
      <div className="App" >
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          {loading ? <p>Loading</p> : <p>{current} Page</p>}
          <div>Child: <Child /></div>
          <div>Child: <Child2 onClick={onClick} /></div>
          <div className="App-operate">
            <button className="App-btn" disabled={loading} onClick={() => change('addASync')}>Add</button>
            <button className="App-btn" onClick={() => change('minus')}>Minus</button>
          </div>
        </header>
      </div >
    </Context.Provider>
  )
}

export default connect(({ login, loading }: LoginModel) => ({
  current: login.current,
  loading: loading.effects['login/addASync']
}))(Login)

复杂的组件中,引入了一个FunCompProps的ts声明:

import H from 'history';
import {
  Dispatch,
} from 'redux';

export interface match<Params extends { [K in keyof Params]?: string } = {}> {
  params: Params;
  isExact: boolean;
  path: string;
  url: string;
}

export interface StaticContext {
  statusCode?: number;
}

export interface FunCompProps<
  Params extends { [K in keyof Params]?: string } = {},
  C extends StaticContext = StaticContext,
  S = H.LocationState
  > {
  dispatch: Dispatch<any>
  history: H.History<S>;
  location: H.Location<S>;
  match: match<Params>;
  staticContext?: C;
}

最后总结: TypeScript并非适用于任何项目,知道即可。 TS可以当成装逼神器,如果JS语法写厌倦了,想试试新的语法,TS还是不错的选择哈。

Headshot of Maxi Ferreira

怀着敬畏之心,做好每一件事。