Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript 珠玑 #23

Open
hawx1993 opened this issue May 31, 2018 · 0 comments
Open

TypeScript 珠玑 #23

hawx1993 opened this issue May 31, 2018 · 0 comments

Comments

@hawx1993
Copy link
Owner

hawx1993 commented May 31, 2018

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant