Skip to content

TypeScript 珠玑 #23

Description

@hawx1993

TypeScript + React

在使用 Typescript 时,React.Component 是一个通用类型 (也被写作 React.Component<PropType, StateType>),所以你实际上需要给它提供 prop 和 state(可选)的类型:

export default class Counter extends React.Component<Props, object> {
  render() {
    const { name, count = 1 } = this.props;
    return (
      <div>
        Counter {name}: {count}
      </div>
    );
  }
}

这里React.Component<Props, object>第一个是props的参数类型, 第二个是state的类型。因为我们暂时用不到state,所以简单放一个objcet类型即可。

无状态组件:

@types/react中已经预定义一个类型type SFC<P>,它也是类型interface StatelessComponent<P>的一个别名,此外,它已经有预定义的children和其他(defaultProps、displayName等等…),所以我们不用每次都自己编写!

import React, { MouseEvent, SFC } from 'react';

type Props = { onClick(e: MouseEvent<HTMLElement>): void };

const Button: SFC<Props> = ({ onClick: handleClick, children }) => (
  <button onClick={handleClick}>{children}</button>
);

使用TS写react组件,与es6的区别是props和state的定义:

interface IProps {
  aProps: string;
  bProps: string;
}
interface IState {
  aState: string;
  bState: string;
}

class App extends React.PureComponent<IProps, IState> {
  state = {
    aState: '',
    bState: '',
  };
}

React.ReactEventHandler<E>

const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... } 

<input onChange={handleChange} ... />

React.MouseEvent<E> | React.KeyboardEvent<E> | React.TouchEvent<E> etc...

const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... }

<div onMouseMove={handleChange} ... />

React.CSSProperties

const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...

Stateful Component

import * as React from 'react';

export interface StatefulCounterProps {
  label: string;
}

interface State {
  readonly count: number;
}

export class StatefulCounter extends React.Component<StatefulCounterProps, State> {
  readonly state: State = {
    count: 0,
  };

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    const { handleIncrement } = this;
    const { label } = this.props;
    const { count } = this.state;

    return (
      <div>
        <span>{label}: {count} </span>
        <button type="button" onClick={this.handleIncrement}>
          {`Increment`}
        </button>
      </div>
    );
  }
}

Generic Components通用组件

import * as React from 'react';

export interface GenericListProps<T> {
  items: T[];
  itemRenderer: (item: T) => JSX.Element;
}

export class GenericList<T> extends React.Component<GenericListProps<T>, {}> {
  render() {
    const { items, itemRenderer } = this.props;

    return (
      <div>
        {items.map(itemRenderer)}
      </div>
    );
  }
}

Interface 和 type

编写库或第三方环境类型定义时,对于公共API总是使用Interface
考虑使用React Component Props和State的类型
interface 可以继承,type不可以

解构赋值的数据类型声明

function getStock() {
    return {
        name: 'bitcoin',
        value: {
            value1: 50000,
            value2: 60000
        }
    }
}

const { name: code, value: { value2 } }: { name: string, value: {value2: number} } = getStock();
console.log(code);// bitcoin
console.log(value2)// 6000

关于函数参数默认值

//  参数默认值最好声明在最后面 
function test(a: string='first', b: string, c: string = 'third') {
    console.log(a)
    console.log(b)
    console.log(c)
}
test('', 'aaa');//  '' aaa third

// 可选参数必须声明在必选参数的后面
function test(a: string='first', b?: string, c: string = 'third') {
    console.log(a)
    console.log(b.length);//  Cannot read property 'length' of undefined
    console.log(c)

}
test();//first 要记得处理可选参数的情况

非空断言标志符

const config = {
  port: 8000
};

if (config) {
  console.log(config.port);
}
// 有了 2.0 提供的 “非空断言标志符”,我们可以这么写了:
console.log(config!.port);

关于命名空间

//pre-compile
namespace Validation {
  const lettersRegexp = /^[A-Za-z]+$/;
  const numberRegexp = /^[0-9]+$/;
}

//post-compile
var Validation;
(function (Validation) {
    var lettersRegexp = /^[A-Za-z]+$/;
    var numberRegexp = /^[0-9]+$/;
})(Validation || (Validation = {})); 

所以namespace也不过就是一个匿名函数的自执行而已,将namespace的名字作为其参数而已

Interface

TypeScript 一共提供 6 种 interface

  • Object interface
interface LabelledValue {
    label: string;
}

function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
  • Index interface
interface CloudDictionary {
    [index: number]: string;// 定义index为number,返回值为string
}

let clouds: CloudDictionary = {};
clouds[0] = 'aws';
  • Class interface

要求 class 需具备哪些 public method 与 property 时,会使用 interface 特別定义。

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

Interface 只能定義 class 的 public 部分,無法定義其 private 與 protected 部分。

  • Constructor interface
  • Function interface
    在 OOP,我們期望 class 該有哪些 public property 與 method,可使用 class interface 描述。
    在 FP,我們會期望 function 該有哪些 signature,可使用 function interface 描述。
interface ILogistics {
    (weight: number): number
}

class ShippingService {
    calculateFee(weight: number, logistics: ILogistics): number {
        return logistics(weight);
    }
}

const shippingService = new ShippingService();
const fee = shippingService.calculateFee(10, weight => 100 * weight + 10);

console.log(fee); // 1010
  • Hybrid interface

对象的类型——接口Interface

接口名一般首字母大写。定义的变量比接口少/多了一些属性都是不允许的。

其实接口就是定义了一个对象有哪些属性,并且属性值是什么类型。如果一个函数返回了一个对象,那么可以用来限定函数返回值类型。

一个接口可以继承多个接口,创建出多个接口的合成接口。

接口通常会根据一个对象是否符合某种特定结构来进行类型检查。当转译成 JavaScript 时,接口会消失 – 它们唯一的目的是在开发阶段里起到辅助的作用。

如果接口需要在其他地方被使用,那么Interface需要export

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

这里,我们定义一个简单接口来对函数自变量进行类型检查:

// Here we define our Food interface, its properties, and their types.
interface Food {
    name: string;
    calories: number;
}
 
// We tell our function to expect an object that fulfills the Food interface. 
// This way we know that the properties we need will always be available.
function speak(food: Food): void{
  console.log("Our " + food.name + " has " + food.calories + " calories.");
}
 
// We define an object that has all of the properties the Food interface expects.
// Notice that types will be inferred automatically.
var ice_cream = {
  name: "ice cream", 
  calories: 200
}
 
speak(ice_cream);

接口类型可以作为方法的参数类型,接口也可以定义方法的类型,和数组类型

interface FuncType {
    (x: string, y: string): string;         // 声明方法成员
}

let func1: FuncType;
func1 = function (prop1: string, prop2: string): string {// 方法参数名称不需要与接口成员的参数名称保持一致
    return prop1 + ' ' + prop2;
}

interface ArrayType {
    [index: number]: string;                // 声明数组成员
}

let arr: ArrayType;
arr = ['Dog', 'Cat'];

TypeScript的接口支持继承与实现。类通过implements关键字继承接口,并实现接口成员。

interface Animal {
    name: string;
    eat(): void;
}

class Dog implements Animal {
    name: string;
    constructor(theName: string) {
        this.name = theName;
    }

    eat() {
        console.log(`${this.name} 吃狗粮。`)
    }
}

let dog: Animal;
dog = new Dog('狗狗');
dog.eat();

可选属性

interface SquareConfig {
  color?: string;
  width?: number;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions