通用无状态组件的类型是什么?或者在typescript中扩展泛型函数接口以进一步通用?

Nam*_*pal 28 javascript generics typescript reactjs

问题:界面Stateless Functional Component给出为

interface SFC<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
}
Run Code Online (Sandbox Code Playgroud)

我的组件的prop类型也是通用的:

interface Prop<V>{
    num: V;
}
Run Code Online (Sandbox Code Playgroud)

如何正确定义我的组件?如:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
Run Code Online (Sandbox Code Playgroud)

在给出了一个错误character 27的是Cannot find name 'T'

这是:Modifiedcript Playground的修改示例

MyFindings:

1:Typescript 2.9.1支持有状态通用组件:http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements

class myCom<T> extends React.Component<Prop<T>, any> {
   render() {
      return <div>test</div>;
   }
}
Run Code Online (Sandbox Code Playgroud)

2:扩展SFC以创建一个新接口,如下面的答案中所述,将使组件的prop类型为any: Typescript使用我不想要的泛型参数/返回类型的React无状态函数.我想为我的道具提供合适的类型

jma*_*eis 19

你不能使用这样的泛型:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
Run Code Online (Sandbox Code Playgroud)

TypeScript规范说明:

形式的构造

< T > ( ... ) => { ... }
Run Code Online (Sandbox Code Playgroud)

可以解析为箭头函数表达式,其类型参数或类型断言应用于没有类型参数的箭头函数.

资源; Microsoft/TypeScript spec.md

您的声明与TypeScript规范中定义的模式不匹配,因此它不起作用.

但是,您可以不使用SFC接口,只需自己声明.

interface Prop<V> {
    num: V;
}

// normal function
function Abc<T extends string | number>(props: Prop<T>): React.ReactElement<Prop<T>> {
    return <div />;
}

// const lambda function
const Abc: <T extends string | number>(p: Prop<T>) => React.ReactElement<Prop<T>> = (props) => {
   return <div />
};

export default function App() {
    return (
        <React.Fragment>
            <Abc<number> num={1} />
            <Abc<string> num="abc" />
            <Abc<string> num={1} /> // string expected but was number
        </React.Fragment>
    );
}
Run Code Online (Sandbox Code Playgroud)


小智 12

有一种模式可以通过在组件外部声明通用组件类型别名,然后在需要时简单地声明它来缓解此问题。

虽然不那么漂亮,但是仍然可以重用和严格。

interface IMyComponentProps<T> {
  name: string
  type: T
}

// instead of inline with component assignment
type MyComponentI<T = any> = React.FC<IMyComponentProps<T>>

const MyComponent: MyComponentI = props => <p {...props}>Hello</p>

const TypedComponent = MyComponent as MyComponentI<number>
Run Code Online (Sandbox Code Playgroud)

  • 这可能不符合泛型的目的。开发人员可能需要重写每个组件的功能并且不能具有共同的行为。`const MyComponent: MyComponentI = props =&gt; &lt;p {...props}&gt;Hello&lt;/p&gt;` 对于每个组件都会重复这一行。 (2认同)

小智 10

@chris 在这里提出的工厂模式很棒,但我不能使用 React Hooks。所以我就用这个。

// Props
interface Props<T> {
  a: T;
}

// Component
export const MyComponent: <T>(p: PropsWithChildren<Props<T>>) => React.ReactElement = props => {
  return <div>Hello Typescript</div>;
};
Run Code Online (Sandbox Code Playgroud)

如果你不需要孩子,你可以删除 PropsWithChildren 部分。道具分解和钩子也可以工作。

export const MyComponent: <T>(p: Props<T>) => React.ReactElement = ({ a }) => {
  const [myState, setMyState] = useState(false);
  return <div>Hello Typescript</div>;
};
Run Code Online (Sandbox Code Playgroud)


Jos*_*e A 9

我提出了一个类似但略有不同的解决方案(与朋友集思广益)。我们试图创建一个 Formik 包装器,并设法使其以下列方式工作:

import React, { memo } from 'react';

export type FormDefaultProps<T> = {
  initialValues: T;
  onSubmit<T>(values: T, actions: FormikActions<T>): void;
  validationSchema?: object;
};

// We extract React.PropsWithChildren from React.FunctionComponent or React.FC
function component<T>(props: React.PropsWithChildren<FormDefaultProps<T>>) {
  // Do whatever you want with the props.
  return(<div>{props.children}</div>
}

// the casting here is key. You can use as typeof component to 
// create the typing automatically with the generic included..
export const FormDefault = memo(component) as typeof component;

Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样使用它:

 <FormDefault<PlanningCreateValues>
        onSubmit={handleSubmit}
        initialValues={PlanningCreateDefaultValues}
      >
         {/*Or any other child content in here */}
        {pages[page]}
</FormDefault>
Run Code Online (Sandbox Code Playgroud)

我无法通过方法表达式实现这一点:

const a: React.FC<MyProp> = (prop) => (<>MyComponent</>);


And*_* Ho 8

我有办法做,但我不确定是否完美

interface ComponentProps<T> {
    text: T;
}

export const Component= <T,>(props: ComponentProps<T>) => {
  const { text } = props
  const [s] = useState(0) // you can use hook
  return (<div>yes</div>)
}

Run Code Online (Sandbox Code Playgroud)

你可以像这样使用组件:

 (
  <>
    <Component<string> text="some text" />
  </>
 )
Run Code Online (Sandbox Code Playgroud)


chr*_*ris 6

工厂模式:

import React, { SFC } from 'react';

export interface GridProps<T = unknown> {
  data: T[];
  renderItem: (props: { item: T }) => React.ReactChild;
}

export const GridFactory = <T extends any>(): SFC<GridProps<T>> => () => {
  return (
    <div>
      ...
    </div>
  );
};

const Grid = GridFactory<string>();
Run Code Online (Sandbox Code Playgroud)