如何划分组件的通用属性并强类型化结果
How to partition a component's generic props and strongly type the results
我正在尝试将自定义类型划分回它的各个元素:
type CustomType<T extends React.ElementType> = React.ComponentPropsWithoutRef<T> & { aBunchOfProps: string; }
代码如下所示:
const partitionProps = <T extends React.ElementType>(
props: CustomType<T>
): {
customProps: { aBunchOfProps: string }, // named type
componentProps: ComponentPropsWithoutRef<T>
} => {
const {
aBunchOfProps,
...componentProps
} = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
} // Error! componentProps: Omit<CustomType<T>, { aBunchOfProps }> is
// not assignable to type ComponentPropsWithoutRef<T>
这很奇怪,因为我能够断言类型相等
type Equals<T, U> = T extends U ? (U extends T : true : false) : false;
type AreTheyEqual<T extends React.ElementType> = Equals<
Omit<CustomType<T>, { aBunchOfProps: string }>,
React.ComponentPropsWithoutRef<T>
>;
type UsingDiv = AreTheyEqual<'div'>; // true
type UsingA = AreTheyEqual<'a'>; // true
type UsingIFrame = AreTheyEqual<'iframe'>; //true
也许应该有一些条件类型来断言分区函数中的类型相等,但我不太明白
type AssertEquality<T extends React.ElementType> = Equals<T1, T2> extends true ? React.ComponentPropsWithoutRef<T> : never;
但这也不太奏效。
有什么想法吗?
编辑 11/1/21:参见 this typescript playground for a reproduction。我们能够强制将 return 类型强制转换为 React.ComponentPropsWithoutRef<T>
,但它仍然保留函数并存在于调用组件内部,如 any
.
这里的问题是你在函数内部使用泛型
const partitionProps = <T extends React.ElementType>(
props: CustomType<T>
): { /** ....some code */ }
T
就像黑盒子,只有在调用函数时才会知道,而比较 :
type UsingDiv = AreTheyEqual<'div'>; // true
type UsingA = AreTheyEqual<'a'>; // true
type UsingIFrame = AreTheyEqual<'iframe'>; //true
更容易,因为 AreTheyEqual
的泛型参数在编译时是已知的:div
、a
、iframe
.
想象一下,我们的函数中没有泛型类型:
const partitionProps = (
props: SomeHelpers & ComponentPropsWithoutRef<'a'>
): {
customProps: { aBunchOfProps: string },
componentProps: ComponentPropsWithoutRef<'a'>
} => {
const {
aBunchOfProps,
...componentProps
} = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
}
没有错误,因为 TS 能够推断出 componentProps
.
的确切类型
一旦您提供了 T
,TS 就不再确定类型是否相等。
因为, return
是不安全的
{
customProps: { aBunchOfProps: string },
componentProps: ComponentPropsWithoutRef<T>
}
但是,您可以放宽严格的行为。您可以重载您的功能。重载是双变的。
type SomeHelpers = { aBunchOfProps: string };
type CustomType<T extends React.ElementType> = SomeHelpers &
React.ComponentPropsWithoutRef<T>;
function partitionProps<T extends React.ElementType<{ tag: number }>>(
props: CustomType<T>
): {
customProps: { aBunchOfProps: string }; // named type
componentProps: ComponentPropsWithoutRef<T>;
};
function partitionProps<T extends React.ElementType>(props: CustomType<T>) {
const { aBunchOfProps, ...componentProps } = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
}
const result = partitionProps({
aBunchOfProps: 'sdf',
tag: 42
});
请记住,您还应该为 React.ElementType
提供通用参数,否则通用参数将为 any
。
见ElementType
签名:
type ElementType<P = any> =
{
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never
}[keyof JSX.IntrinsicElements] |
ComponentType<P>;
我正在尝试将自定义类型划分回它的各个元素:
type CustomType<T extends React.ElementType> = React.ComponentPropsWithoutRef<T> & { aBunchOfProps: string; }
代码如下所示:
const partitionProps = <T extends React.ElementType>(
props: CustomType<T>
): {
customProps: { aBunchOfProps: string }, // named type
componentProps: ComponentPropsWithoutRef<T>
} => {
const {
aBunchOfProps,
...componentProps
} = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
} // Error! componentProps: Omit<CustomType<T>, { aBunchOfProps }> is
// not assignable to type ComponentPropsWithoutRef<T>
这很奇怪,因为我能够断言类型相等
type Equals<T, U> = T extends U ? (U extends T : true : false) : false;
type AreTheyEqual<T extends React.ElementType> = Equals<
Omit<CustomType<T>, { aBunchOfProps: string }>,
React.ComponentPropsWithoutRef<T>
>;
type UsingDiv = AreTheyEqual<'div'>; // true
type UsingA = AreTheyEqual<'a'>; // true
type UsingIFrame = AreTheyEqual<'iframe'>; //true
也许应该有一些条件类型来断言分区函数中的类型相等,但我不太明白
type AssertEquality<T extends React.ElementType> = Equals<T1, T2> extends true ? React.ComponentPropsWithoutRef<T> : never;
但这也不太奏效。
有什么想法吗?
编辑 11/1/21:参见 this typescript playground for a reproduction。我们能够强制将 return 类型强制转换为 React.ComponentPropsWithoutRef<T>
,但它仍然保留函数并存在于调用组件内部,如 any
.
这里的问题是你在函数内部使用泛型
const partitionProps = <T extends React.ElementType>(
props: CustomType<T>
): { /** ....some code */ }
T
就像黑盒子,只有在调用函数时才会知道,而比较 :
type UsingDiv = AreTheyEqual<'div'>; // true
type UsingA = AreTheyEqual<'a'>; // true
type UsingIFrame = AreTheyEqual<'iframe'>; //true
更容易,因为 AreTheyEqual
的泛型参数在编译时是已知的:div
、a
、iframe
.
想象一下,我们的函数中没有泛型类型:
const partitionProps = (
props: SomeHelpers & ComponentPropsWithoutRef<'a'>
): {
customProps: { aBunchOfProps: string },
componentProps: ComponentPropsWithoutRef<'a'>
} => {
const {
aBunchOfProps,
...componentProps
} = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
}
没有错误,因为 TS 能够推断出 componentProps
.
一旦您提供了 T
,TS 就不再确定类型是否相等。
因为, return
{
customProps: { aBunchOfProps: string },
componentProps: ComponentPropsWithoutRef<T>
}
但是,您可以放宽严格的行为。您可以重载您的功能。重载是双变的。
type SomeHelpers = { aBunchOfProps: string };
type CustomType<T extends React.ElementType> = SomeHelpers &
React.ComponentPropsWithoutRef<T>;
function partitionProps<T extends React.ElementType<{ tag: number }>>(
props: CustomType<T>
): {
customProps: { aBunchOfProps: string }; // named type
componentProps: ComponentPropsWithoutRef<T>;
};
function partitionProps<T extends React.ElementType>(props: CustomType<T>) {
const { aBunchOfProps, ...componentProps } = props;
const customProps = { aBunchOfProps };
return { customProps, componentProps };
}
const result = partitionProps({
aBunchOfProps: 'sdf',
tag: 42
});
请记住,您还应该为 React.ElementType
提供通用参数,否则通用参数将为 any
。
见ElementType
签名:
type ElementType<P = any> =
{
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never
}[keyof JSX.IntrinsicElements] |
ComponentType<P>;