如何在TypeScript中键入"as" JSX属性? [英] How do I type this 'as' JSX attribute in TypeScript?

查看:102
本文介绍了如何在TypeScript中键入"as" JSX属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在描述一个React库,该库通过名为as的属性来获取组件或HTML标签的名称.赋予as属性后,它将根据该组件/标签名称创建一个元素,并传递所有其他给定的属性.

I'm describing a React library that takes a component or HTML tag name through an attribute called as. When given the as attribute, it creates an element from that component/tag name, and passes any other given attributes along.

以下是一些示例:

<Foo as="a" href="https://example.com" />
<Foo as={FancyButton} fancyButtonAttr="hello!" />

我知道语义UI在增强方面起到了类似作用.我将如何在TypeScript中键入此内容?

I know that Semantic UI does something similar with augmentations. How would I go about typing this in TypeScript?

推荐答案

我将在此处给出最基本要求的示例.您可以尝试将其概括为功能更复杂的产品.

I'll give an example of the most basic requirements given here. You can try to generalize to something that does something more sophisticated.

首先,这是我们的魔法组件!

First, here's our magic component!

import * as React from "react";

function Foo<Tag extends AnyTag>(props: { as: Tag } & PropsOf<Tag>): JSX.Element;

请注意两件事:

  • 一种称为AnyTag
  • 的类型
  • 称为PropsOf
  • 的实用程序类型
  • A type called AnyTag
  • A utility type called PropsOf

那是我们的公开签名.我们也许可以使用该签名以类型安全的方式来实现它,但是我们可以在实现签名中作弊"一些.作为实施者,这取决于您.

That was our public signature. We might be able to implement this in a type-safe way using that signature, but we can "cheat" a little here in the implementation signature. This is up to you as the implementer.

function Foo(props: any) {
    return <div>Implementation goes here!</div>
}

让我们回到我们提到的这两种类型. AnyTag是JSX标签可以的任何内容.

Let's go back to those two types we mentioned. AnyTag is anything that a JSX tag can be.

type AnyTag = string
            | React.FunctionComponent<never>
            | (new (props: never) => React.Component);

PropsOf尝试获取给定HTML标记名称或组件的预期属性.

PropsOf tries to get the expected properties for a given HTML tag name or component.

type PropsOf<Tag> =
    Tag extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[Tag] :
    Tag extends React.ComponentType<infer Props> ? Props & JSX.IntrinsicAttributes :
    never
;

现在让我们定义几个具有相同道具的组件-一个函数和一个类.

Let's now define a few components taking the same props - one function and one class.

interface SomeProps {
  x: boolean; y: boolean; z: boolean;
}

function Bar(props: SomeProps) {
    return <div>{props.x} {props.y} {props.z}</div>;
}

class Baz extends React.Component<SomeProps> {
    render() {
        const { x, y, z } = this.props;
        return <div>{x} {y} {z}</div>;
    }
}

现在这里有一些用法!

let a1 = <Foo as="a" href="https://kthxb.ai" />;         // good!
let a2 = <Foo as="div" href="https://kthxb.ai" />;       // error!
let a3 = <Foo as="a" href={100} />;                      // error!

let b1 = <Foo as={Bar} x y z />;                         // good!
let b2 = <Foo as={Bar} x y z asdsadsada />;              // error!
let b3 = <Foo as={Bar} x={1} y={2} z={3} asdsadsada />;  // error!

let c1 = <Foo as={Baz} x y z />;                         // good!
let c2 = <Foo as={Baz} x y z asdsadsada />;              // error!
let c3 = <Foo as={Baz} x={1} y={2} z={3} asdsadsada />;  // error!

一起

import * as React from "react";

// Here's our magic component!
// Note two things:
//   - A type called AnyTag
//   - A utility type called PropsOf
function Foo<Tag extends AnyTag>(props: { as: Tag } & PropsOf<Tag>): JSX.Element;

// That was our public signature. We might be able to implement this in a type-safe way using that signature,
// but we can "cheat" a little here in the implementation signature. This is up to you as the implementer.
function Foo(props: any) {
    return <div>Implementation goes here!</div>
}

// AnyTag is anything that a JSX tag can be.
type AnyTag = string
            | React.FunctionComponent<never>
            | (new (props: never) => React.Component);

// PropsOf tries to get the expected properties for a given HTML tag name or component.
type PropsOf<Tag> =
    Tag extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[Tag] :
    Tag extends React.ComponentType<infer Props> ? Props & JSX.IntrinsicAttributes :
    never
;

// Let's now define a few components taking the same props - one function and one class.

interface SomeProps {
  x: boolean; y: boolean; z: boolean;
}

function Bar(props: SomeProps) {
    return <div>{props.x} {props.y} {props.z}</div>;
}

class Baz extends React.Component<SomeProps> {
    render() {
        const { x, y, z } = this.props;
        return <div>{x} {y} {z}</div>;
    }
}

// Now here's some usage!

let a1 = <Foo as="a" href="https://kthxb.ai" />;         // good!
let a2 = <Foo as="div" href="https://kthxb.ai" />;       // error!
let a3 = <Foo as="a" href={100} />;                      // error!

let b1 = <Foo as={Bar} x y z />;                         // good!
let b2 = <Foo as={Bar} x y z asdsadsada />;              // error!
let b3 = <Foo as={Bar} x={1} y={2} z={3} asdsadsada />;  // error!

let c1 = <Foo as={Baz} x y z />;                         // good!
let c2 = <Foo as={Baz} x y z asdsadsada />;              // error!
let c3 = <Foo as={Baz} x={1} y={2} z={3} asdsadsada />;  // error!

这篇关于如何在TypeScript中键入"as" JSX属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆