将 class 名称注入样式化组件时的输入

Typings when injecting class name into styled components

请帮我修正打字问题。

我想将 class 名称注入样式组件。 我做了这个功能:

type IntrinsicElementsKeys = keyof { div: any, a: any } /* could be keyof JSX.IntrinsicElements */

export function elementWithClass(e: IntrinsicElementsKeys, customClassName: string) {
    /* [2] if I don't force the type ts does not allow me to call se.attrs() */
    const se = styled[e] as ThemedStyledFunction<typeof e, any>
    
    const res = se.attrs(props => ({
        className: customClassName + " " + (props.className || "")
    }))``

    /* [1] if I don't force "any" then I cannot use native props such as "href" */
    return res as any;
}

我想去掉 [1] 处的“任何”,但如果我这样做并按如下方式调用组件:

const Link = elementWithClass("a", "rick-roll")

const MyElement: React.FC<{ /* props */ }> = props => <Link href="http://rick-roll.com">rick</Link>

然后 TS 抱怨“href”是一个未知的 props。

所以我的问题是:如何修复我的打字?

注意 1: 如果我删除 [2] 处的“as”,那么 TS 会抱怨

Each member of the union type '(<U, NewA extends Partial<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & U> & { ...; } = {}>(attrs: Attrs<...>) => ThemedStyledFunction<...>) | (<U, NewA extends Partial<...> & { ...; } = {}>(attrs: Attrs<...>) => ThemedStyledFunction<...>)' has signatures, but none of those signatures are compatible with each other.

注2:此题与 但更具体地说明了如何使用我使用的特定方法修复输入。

所以您首先应该想到的是,如果您希望 return 类型根据输入而不同,您很可能应该 包括泛型 .

所以,让我们来解决这个问题!

type IntrinsicElementsKeys = "div" | "a"; /* could be keyof JSX.IntrinsicElements */

// 1st step, let's introduce the generic inside the function
export function elementWithClass<T extends IntrinsicElementsKeys>(e: T, customClassName: string) {

// 2nd step, let's pass it in ThemedStyledFunction
    const se = styled[e] as ThemedStyledFunction<T, any>;

// 3rd step, pass in the generic into the `attrs` function
    const res = se.attrs<T>(props => ({
        className: customClassName + " " + (props.className || "")
    }))``

    /* [1] if I don't force "any" then I cannot use native props such as "href" */
    return res;
}

const Link = elementWithClass("a", "rick-roll")
const MyElement: React.FC<{ /* props */ }> = props => <Link href="http://rick-roll.com">rick</Link> // all good

const Link1 = elementWithClass("div", "rick-roll")
const MyElement1: React.FC<{ /* props */ }> = props => <Link1 href="http://rick-roll.com">rick</Link1> // error

只需按照以下步骤,即可推断出道具类型!

  1. 引入一个泛型来接受元素;
  2. 将泛型添加到 ThemedStyledFunction;
  3. 将泛型添加到 attrs 函数;

这里有一个playground link,你也可以用它来玩。