打字稿 - 如何省略属性,以便可以使用点差传输子集?

问题描述 投票:2回答:3

版本:Typescript 3和React 16。

我想传递自定义属性并允许覆盖我的组件上的所有标准HTML属性。

组件用法:

<ExamplePropTransfer specialProp={"test"} style={{color:'blue'}}/>

ExamplePropTransfer:

import * as React from "react"
import {HTMLProps, PureComponent} from "react";

export interface ExampleProps{
  specialProp: string;
  // and possibly many more
}

export class ExamplePropTransfer
extends PureComponent<ExampleProps & HTMLProps<HTMLSpanElement>> {

  render(){
    console.log("specialProp: " + this.props.specialProp);

    // (A)
    // warning.js:33 Warning: React does not recognize the
    // `specialProp` prop on a DOM element.
    // return <span {...(this.props as HTMLProps<HTMLSpanElement>)}>
    //   Example Content
    // </span>

    // (B)
    let {specialProp, ...htmlProps} = this.props;
    return <span {...htmlProps}>Example Content</span>
  }
}

我能够完成这项工作的唯一方法是(B)在上面的示例代码中。

但这很容易出错,因为我在重复自己。每当我在ExampleProps中添加/删除成员时,我都必须维护解构声明。

整个解构说明是多余的。我不需要引用各个属性 - 我只是将它剥离我的自定义属性,因此它们不会作为span属性传递。

我尝试做(A),但仍然将specialProps传递给span并导致React记录上面的警告节目。

我试图告诉Typescript“转移除了在ExampleProps上定义的那些属性之外的所有属性”,而不必明确列出它们?

reactjs typescript
3个回答
1
投票

除非您使用元数据反射,否则在运行时不存在接口,因此在运行时无法“传输除ExampleProps上定义的属性之外的所有属性”。我不熟悉元数据反射以举例说明,但是这里有一种替代方法,它既可以提供ExampleProps类型,也可以在运行时删除道具而无需重复:

import * as React from "react"
import {HTMLProps, PureComponent} from "react";
import _ from "underscore";

type Placeholder<T> = {placeholder: T};
function p<T>() { return {} as Placeholder<T>; }
const examplePropsSpec = {
  specialProp: p<string>()
};

export type ExampleProps = {
  [K in keyof typeof examplePropsSpec]:
  (typeof examplePropsSpec)[K] extends Placeholder<infer T> ? T : never;
};

export class ExamplePropTransfer
extends PureComponent<ExampleProps & HTMLProps<HTMLSpanElement>> {

  render(){
    console.log("specialProp: " + this.props.specialProp);

    let htmlProps = _.omit(this.props, Object.keys(examplePropsSpec));
    return <span {...htmlProps}>Example Content</span>
  }
}

0
投票

Matt的答案非常棒,但我认为它可以简化一点(不需要占位符类型):

function p<T>() { return null as any as T; }
const examplePropsSpec = {
  specialProp: p<string>(),
};

export type ExampleProps = {
  [K in keyof typeof examplePropsSpec]: (typeof examplePropsSpec)[K];
};


let k: ExampleProps = {
  specialProp: "k"
};

0
投票

如果我们希望在运行时不使用反射存在TypeScript接口,我们可以创建它,使用对象文字来保存接口中的成员。它很脆弱,但编译器会告诉我们接口是否已更改,以便我们可以调整接口对象。

然后,我们可以过滤一个对象,只保留其属性在接口中的哪些键。

POC:

// @types/react/index.d.ts

interface HTMLProps<T> {
    accept?: string;
    acceptCharset?: string;
    action?: string;
    // ... see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/10a19353e01f9cd275d4efda9be386c3dc7d6115/types/react/index.d.ts
}

// Our code

type Required<T> = {
    [P in keyof T]: T[P];
}

const allHTMLProps: Required<HTMLProps<any>> = {
    accept: '',
    acceptCharset: '',
    action: ''
    // ...
};

const allHTMLPropsKeys = Object.keys(allHTMLProps);

function filterHTMLProps<P, O>(o: O & HTMLProps<T>) {
    const result: HTMLProps<T> = {};
    Object.keys(o).forEach(k => {
        if (allHTMLPropsKeys.indexOf(k) >= 0) {
            result[k] = o[k];
        }
    });
    return result;
}

const source = { accept: 'a', ko: 1 };
console.log(filterHTMLProps(source)); // { accept: 'a' }
© www.soinside.com 2019 - 2024. All rights reserved.