问题:Stateless Functional Component
的界面为
interface SFC<P = {}> {
(props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
propTypes?: ValidationMap<P>;
}
我组件的prop类型也通用为:
interface Prop<V>{
num: V;
}
如何正确定义我的组件?为:
const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
在character 27
处出现一个错误,提示Cannot find name 'T'
这里是:Typescript Playground of modified example
我的发现:
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>;
}
}
2:扩展SFC
来创建新接口,如以下答案所述,将使组件的prop类型为any
:
Typescript React stateless function with generic parameter/return types我不想要。我想给我的道具加上合适的字体
答案 0 :(得分:10)
您不能使用这样的泛型:
const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
TypeScript规范规定:
形式的构造
< T > ( ... ) => { ... }
可以被解析为带有类型参数的箭头函数表达式,或者被应用到没有类型参数的箭头函数的类型断言。
source; 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>
);
}
答案 1 :(得分:3)
工厂模式:
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>();
答案 2 :(得分:3)
有一种缓解此问题的模式,方法是在组件外部声明通用组件类型别名,然后在需要时简单地声明它。
不漂亮,但仍然可重用和严格。
import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(512, activation=tf.nn.relu),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)
答案 3 :(得分:0)
你有这个:
interface Prop<V> {
num: V;
}
您的组件定义如下:
const myCom: SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
这不起作用,因为您需要在界面中为V
提供具体类型,因为您正在组件中实现它。
看起来像这样:
const myCom: SFC<Prop<object>> = <T>(props: Prop<T>)=> <div>test</div>
请注意,我在有object
的地方使用T
。这只是一个例子。
答案 4 :(得分:0)
您可以放弃使用React.FC
进行注释,只需写:
const myCom = <T>(props: Prop<T>) => <div>test</div>
答案 5 :(得分:0)
我正在提出一种类似但略有不同的解决方案(与朋友头脑风暴)。我们试图创建一个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;
然后,您可以像这样使用它:
<FormDefault<PlanningCreateValues>
onSubmit={handleSubmit}
initialValues={PlanningCreateDefaultValues}
>
{/*Or any other child content in here */}
{pages[page]}
</FormDefault>
我无法通过方法表达式来实现:
const a: React.FC<MyProp> = (prop) => (<>MyComponent</>);
答案 6 :(得分:0)
根据jmattheis的帖子的通用无状态组件示例。
import React from "react";
type Prop<T> = {
example: T;
};
const MyGenericStatelessComponent: <T extends Record<string, number | string>>(props: Prop<T>) => JSX.Element = <
T extends Record<string, unknown>
>(
props: Prop<T>
): JSX.Element => {
return (
<div>
Example Prop id: {props.example.id}, Example Prop name: {props.example.name}
</div>
);
};
export default MyGenericStatelessComponent;
<MyGenericStatelessComponent example={{ id: 1, name: "test01" }} />
答案 7 :(得分:0)
@chris在这里展示的Factory模式很棒,但是我不能使用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>;
};
如果不需要孩子,可以删除PropsWithChildren部分。 道具分解和挂钩也起作用。
export const MyComponent: <T>(p: Props<T>) => React.ReactElement = ({ a }) => {
const [myState, setMyState] = useState(false);
return <div>Hello Typescript</div>;
};
答案 8 :(得分:0)
使用 T = any
作为 @vadistic 示例有效,但您不会进行任何类型检查。使用此代码,您将进行代码完成和类型检查。
interface IProps<TModel> extends RouteComponentProps {
headerText?: string | React.ReactNode;
collection: TModel[];
}
interface ICommonSortableType extends ISortableItem {
id: number;
isCorrectResponse: boolean;
}
interface ISortableItem {
sortableId: number;
}
type GenericFunctionalComponent<TModel> = React.FC<IProps<TModel>>;
const CommonSortableList: GenericFunctionalComponent<ICommonSortableType> = (props) => {
...
}
然后可以这样使用:
class CommonSortableType {
public sortableId: number = -1;
public id: number = -1;
public isCorrectResponse: boolean = false;
}
<CommonSortableList
collection={item.commonSortableTypes} //Is CommonSortableType[]
headerText={<FormattedMessage id="item.list" />}
</CommonSortableList>
class ExtendedOptionDto extends OptionDto implements ICommonSortableType {
public sortableId: number = -1;
}
class OptionDto {
public id: number = -1;
public isCorrectResponse: boolean = false;
}
<CommonSortableList
collection={item.extendedOptionDtos} //Is ExtendedOptionDto[]
headerText={<FormattedMessage id="item.list" />}
</CommonSortableList>