TypeScript 在JSX元素上使用类型推断时性能降低

mec1mxoz  于 2022-10-29  发布在  TypeScript
关注(0)|答案(8)|浏览(221)

版本

类型脚本4.2.4
输入4.3.0-dev.20210417

预期行为

Box组件所做的任何更改都具有快速验证和自动完成关联属性的功能。

实际行为

TypeScript验证和自动完成速度缓慢。

说明

我一直在研究当前的趋势,即创建一个通用的React元素,该元素将通过element属性和相关属性并调用React.createElement。类似的方法被一些著名的项目使用,如Material UIGithub Primer。我希望确保TypeScript将从给定的element属性推断出正确的属性。我编写的代码确实可以工作,但是它似乎对性能有很大的影响。

相关

此问题可能与以下问题相关:第一个是第二个是第三个是第四个

示例

一个可重现的小示例可在以下网址找到:https://github.com/scottwillmoore/react-box。您也可以在CodeSandbox中进行试验。CodeSandbox版本有一些不同。
下面是TypeScript的主要内容:

import React from "react";

type ElementType = keyof JSX.IntrinsicElements;

type ElementProperties<T extends ElementType> = JSX.IntrinsicElements[T];

type WithElement<T extends ElementType> = {
    element: T;
} & ElementProperties<T>;

type WithChildren = {
    children?: React.ReactNode;
};

type BoxProperties<T extends ElementType> = WithElement<T> & WithChildren;

function Box<T extends ElementType>({ element, children, ...properties }: BoxProperties<T>) {
    return React.createElement(element, properties, children);
}

export default function App() {
    return (
        <Box element="a" href="#">
            Hello, world!
        </Box>
    );
}
x0fgdtte

x0fgdtte1#

快速更新,我已将实现更改为:

type Join<T, U> = T & Omit<U, keyof T>;

type As = React.ElementType;

type AsProps<T extends As> = React.ComponentPropsWithoutRef<T>;

type WithAs<T extends As> = Join<{ as: T }, AsProps<T>>;

type WithChildren = {
    children?: React.ReactNode;
};

type BoxProps<T extends As> = WithAs<T> & WithChildren;

function Box<T extends As>({ as, children, ...properties }: BoxProps<T>) {
    return React.createElement(as, properties, children);
}

export default function App() {
    return (
        <Box as="a" href="#">
            Hello, world!
        </Box>
    );
}

这是基于Chakra UI implementation的,它似乎大大加快了验证和自动完成。如果你排除了Join,它也不起作用,我觉得这不应该有什么区别...
即使简化为:

type BoxProps<T extends As> = { as: T } & Omit<React.ComponentPropsWithoutRef<T>, never>;

一旦Omit被移除,推论就不起作用了。有人知道为什么会发生这种情况吗?

xqk2d5yq

xqk2d5yq2#

检查次数:
TS 3.7 - 1.0秒
TS 3.8 - 1.1秒
TS 3.9 - 1.5秒
它看起来在之前和之后都很稳定。注意,对于这么多的代码来说,即使是3. 7也很慢。

cigdeys3

cigdeys34#

哦,对不起,我应该提到它不是性能退化,而是一个需要改进的领域,因为这种模式在几个流行的React组件库中相当流行。我将研究#41821,它看起来确实很相似。另外,你知道为什么在我的第二个示例中省略Omit会导致类型推理不起作用吗?

dm7nw8vv

dm7nw8vv5#

不用担心,检查回归只是性能调查的正常部分。
只是为了设置期望值,ElementType是一个巨大的联合体,几乎总是会导致性能问题。我会看看这个特定代码中是否有什么可以改进的地方,但是,正如你在链接的bug中看到的,这已经得到了很多关注。
我对这些类型不是很熟悉,所以你需要更具体地回答你关于emit的问题。我试着用你的第二个实现替换你建议的BoxProps,然后从<Box>元素中删除href属性,补全建议把它加回去。你是指别的吗?
编辑:我忘了删除Omit .🤦‍ ♂️

okxuctiv

okxuctiv6#

下面是一个更简单的版本,但我仍然不知道为什么会出现错误:

type ComponentProps<T extends "symbol"> =
    T extends string
        ? JSX.IntrinsicElements[T]
        : never;

function F<T extends "symbol">(p1: Pick<ComponentProps<T>, keyof ComponentProps<T>>, p2: ComponentProps<T>) {
    p1 = p2; // Fine
    p2 = p1; // Error
}
x6492ojm

x6492ojm7#

至少对于我的简化版本,我认为问题是编译器没有利用T extends "symbol"隐含T extends string的事实,所以ComponentProps<T>永远不会是never(如果是,p2 = p1将是一个错误)。
这就解释了为什么Omit<Type1, never>Type1不一样。我还没有完全了解它为什么会影响示例中的智能感知,但我相信答案是相似的-编译器并不完全确定类型,因此它不提供成员。

8cdiaqws

8cdiaqws8#

这真的很奇怪,谢谢你调查这个问题。我想我明白你的逻辑,如果T extends "symbol",编译器不知道T extends string是隐含的,因此p2: ComponentProps<T>,其中T extends "symbol"有条件地变成never。我仍然不太明白Omit是如何修复这个问题的,但我有一些空闲时间快速浏览了一下其他TypeScript问题,并发现了这个问题#36981,这导致我提出了这个拉取请求#42524,它可能相关,也可能不相关。

相关问题