不带 React 的 TypeScript JSX

2024-04-07

我想在 TypeScript 中使用 JSX 语法,但不想使用 React。

我在这里看到了其他相关问题的答案,但没有任何内容足够完整或详细,无法提供任何帮助。我读了本指南 https://basarat.gitbooks.io/typescript/content/docs/jsx/tsx.htmlJSX章节 https://www.typescriptlang.org/docs/handbook/jsx.html手册上的内容,并没有多大帮助。我不明白语言功能本身是如何工作的。

我尝试检查 React 声明,但它对我来说太大了,无法吸收 - 我需要一个最小的、有效的示例来演示类型检查的元素、组件和属性。 (它不需要工厂函数的有效实现,我最感兴趣的是声明。)

我至少想要一个能够实现以下工作且具有类型安全性的示例:

var el = <Ping blurt="ya"></Ping>;

var div = <div id="foo">Hello JSX! {el}</div>;

我想我需要声明JSX.IntrinsicElements至少,和一个createElement()某种工厂功能,但据我所知,这就是:

declare module JSX {
    interface IntrinsicElements {
        div: flurp.VNode<HTMLDivElement>;
    }
}

module flurp {

    export interface VNode<E extends Element> {
        id: string
    }

    export function createElement<T extends Element>(type: string, props?: any, ...children: (VNode<Element>|string)[]): VNode<Element> {
        return {
            id: props["id"]
        };
    }
}

class Ping {
    // ???
}

var el = <Ping blurt="ya"></Ping>;

var div = <div id="foo">Hello JSX! {el}</div>;

我编译这个tsconfig.json like:

{
    "compilerOptions": {
        "target": "es5",
        "jsx": "react",
        "reactNamespace": "flurp"
    }
}

悬停在<div>元素和id="foo"属性,我可以看到编译器理解我的内在元素声明 - 到目前为止,一切都很好。

现在,要获得<Ping blurt="ya">编译声明,并进行类型检查blurt属性,我该怎么办?应该Ping甚至是一个class或者它可能是一个function或者其他的东西?

理想情况下,元素和组件应该具有某种共同的祖先,因此我可以拥有一组适用于两者的共同属性。

我正在寻找创造一些简单而轻量级的东西,也许沿着蒙莓 http://monkberry.js.org/,但使用 JSX 语法,例如使用类似的组件<If> and <For>而不是内联语句。这可行吗? (有没有类似的现有项目可供我参考?)

请,如果有人可以提供一个如何使用语言功能本身的有效的、最小的示例,那将是一个巨大的帮助!


确实,TypeScript 手册的 JSX 部分是一个很好的资源 https://www.typescriptlang.org/docs/handbook/jsx.html;对于那些还没有看过的人,请检查一下。

如果你想tsc要编译 JSX,您需要执行以下操作(极其广泛的概述):

  1. 声明IntrinsicElements接口/类型JSX命名空间(启用类型安全)。
  2. 创建一个 JSX 工厂函数。
  3. 配置您的消费应用程序tsconfig to use "react"代码生成并指向您的 JSX Factory(使用"jsxFactory"选项)。
  4. 将 JSX 工厂函数导入到.tsx file.

下面对此进行更深入的研究。还,您可能想查看存储库 https://github.com/ConnorJamesLow/texsaur或者可能一个工作示例/游乐场 https://www.typescriptlang.org/play?target=99#code/PQKgsAUABCUAICsDOAPKyWRsSw9QB4AnAUwDMTSA7AYxKgBsBLAIwF4AiAEQHkBZDlGAA+SJAAmJGgwCGpKFRkBbEkgAOMulABSAZQAaUAN5YoZ-ABUAFvVIAXAK5EqUOwE819APZkoXpzoGUABimnZeRG6mZu6eUACiDCQqVHZQbFAAEhZ8ADKJySSpANxi0GZCwFAAkqlETFRITDQFKXZ8MmpQAOZEMixIUDIMDK42UEh2MlTicuJZObmuMt2DDWP0FrpQvHyMrAB00VANdpRkmvS1dvWNza1FdoMkKGczg9e3TS1JbR1dRigAF8ysdLOMyF4Rl4AO4NbpDeQ0ByTLxKVweVQAGgUXjSGiIaR8UC2AHJBgBrKiwlx6QyKFTqS4ALmOsSudQa3weqX+6WMxwqUAA2gBpE4uCkkNzE7J5Hl2CwrAByyhI-wAusyBeUhXqRRTtZNbt0tUMqFFdXqQVabWzMSSVvypTLfHSDp8ufdfo8kKUIMdTudLlAAMJotReKiPHX6swACjURC8nkJTFUAH5tYDhS6jTd4Wbpm5gTiaFYmAxxNQs1BlV5JMKNQBKbX1yTHG12iBkBy0OxMKPoVAECxQF5vcSDd1KhEZGcrYTxqbdbUWHEyOwFlgOM5IbMG6X5k1Fi3AqAAHwUDhGOIO9-LlerRTbDZITdbgX0BwVkF7-cHFwMGXFZtXdcMlEjaNUg3Ld6h3PdtQABTkNUziIJACHZYkV2ES9r1vKB7wOR8q2oV9GxbCiSD-PsaAHIdgJXMCDAOWd8PAiMo0eWDt13VQD1zI8JgLKhTW1YtzyvKgbwYO8HwrMiXzrN8P1jComF8ZdMRwp02H0qBSX-ejANJZt1P1ewnBcFd403Pi9ygDMM2MIEy0U58qGbf0hRtIV2UdOcMU8XTuh8ioaCjSZxx9VJtTlfJYsVFU1X+YVZw1flxC8ZE2hI0hNxIBUQO6byyiFfAAEEkCaboXHs+D+P3Y4kjSJROn5Oy4NYJqnJcowgTKq1WqgJMU21F0cJ03x2rUcKzEhIgoETZMumJeMeBYBApDsA4XSQeNZvMmRBmLZtzJMAMrQqqp4lePp6KgAA3YYHAEwUKjGroMi+g5wl0UTunjIa4ygSLGjSF6GDe-lZuFL7MpO803HmvVwei4sACVyEoIotAyEgkqGU6LVRoVNOW7DfCxnHqDoeHVsy-SMlJPtJDIBoSHEMyLNByooGqoYoAuEYWE0CleOSNQ0nCCYSDSaYhm6hCSFZa640JwpUgOJB5cq5X+JWlMcSht6QbjIEYt13nQZpig6ffBH+VNkgyYqPz3fKip8E6TwZjBjzqGORblpG0j5mJcPqAuj6zAp7SQt8cP0gM8lAZ5y6+YqTW8oaaMiAsCcoAAagycO3f1cGBxk13Y+BOv48qog+jcA4mCQJuW-jcPzptjWkoOX2inEeNiJ7iu0ajau3onj39Rzx5B7UP3xFDDzu4882zDnqArOcGKtbsf1u2MhiXCQ+F40BFhocJYEDxvpw7GPeFgRjq095cAhxCYJ7hCMR+hIgQEGAD-P+kBuwvSWoTfkBAL5iSgIAuwnA3AyA4MIEB8DujCH9JAKBUAwGwMIUwcQnBIReHQZkQmDAvBfgAITGEJsA0Bv8cFlGyrlReLAGytyHjMNeT54xgObEAA.

JSX 命名空间

在这里,您告诉 TypeScript 如何解释您的 JSX 代码。您至少需要声明IntrinsicElements,但是您可以声明其他类型,这些类型将为您提供更好的类型提示、启用组件功能,并通常改进/调整 TypeScript 对 JSX 的理解方式。

以下是 JSX 命名空间的声明示例:

/// <reference lib="DOM" />

declare namespace JSX {
    // The return type of our JSX Factory: this could be anything
    type Element = HTMLElement;

    // IntrinsicElementMap grabs all the standard HTML tags in the TS DOM lib.
    interface IntrinsicElements extends IntrinsicElementMap { }


    // The following are custom types, not part of TS's known JSX namespace:
    type IntrinsicElementMap = {
        [K in keyof HTMLElementTagNameMap]: {
            [k: string]: any
        }
    }

    interface Component {
        (properties?: { [key: string]: any }, children?: Node[]): Node
    }
}

一些注意事项:

  • 如果您打算与HTMLElements and Node是,DOM 库 (lib.dom.d.ts)是获得更好的类型检查的重要资源。在此示例中,我使用它来将所有真实的 HTML 标记声明为有效的内部元素。您可以进一步扩展它,例如预加载EventHandlers like onclick via GlobalEventHandlers.
  • Element是 TypeScript 在 JSX 命名空间中查找的另一种类型。这是可选的(默认为any)。使用它来指定工厂函数的返回类型。
  • 我们可以使用此定义创建一个简单的基于函数的组件(我已经使用Component界面。
  • 对于类组件,我们需要声明JSX.ElementClass界面。看here https://www.typescriptlang.org/docs/handbook/jsx.html#class-component更多细节。

JSX 工厂函数

The JSX命名空间帮助我们定义如何在代码中使用 JSX,但我们仍然需要实现一个可以处理由tsc。我们的工厂函数应该遵循以下形式(tag: string, properties: { [k: string]: any }, ...children: any[]): any,或更具体的东西。这是一个启用功能组件的示例(注意,它又大又丑):

function jsx(tag: JSX.Tag | JSX.Component, 
             attributes: { [key: string]: any } | null, 
             ...children: Node[]) 
{

    if (typeof tag === 'function') {
        return tag(attributes ?? {}, children);
    }
    type Tag = typeof tag;
    const element: HTMLElementTagNameMap[Tag] = document.createElement(tag);

    // Assign attributes:
    let map = (attributes ?? {});
    let prop: keyof typeof map;
    for (prop of (Object.keys(map) as any)) {

        // Extract values:
        prop = prop.toString();
        const value = map[prop] as any;
        const anyReference = element as any;
        if (typeof anyReference[prop] === 'undefined') {
            // As a fallback, attempt to set an attribute:
            element.setAttribute(prop, value);
        } else {
            anyReference[prop] = value;
        }
    }

    // append children
    for (let child of children) {
        if (typeof child === 'string') {
            element.innerText += child;
            continue;
        }
        if (Array.isArray(child)) {
            element.append(...child);
            continue;
        }
        element.appendChild(child);
    }
    return element;

}

TSX 和 tsconfig

现在,剩下的就是使用我们的jsx功能。我们的 tsconfig 至少应该配置以下内容:

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "jsx",
  }
}

现在,我们可以在任何地方编写 JSX.tsx文件只要我们导入我们的jsxFactory:

import jsx from './jsxFactory';

function Ping({ blurt }: { blurt: string }) {
    return <div>{blurt}</div>
}

var el = <Ping blurt="ya"></Ping>;

// var div: HTMLElement
var div = <div id="foo">Hello JSX! {el}</div>;

document.body.appendChild(div)

谢谢JSX.Element,TypeScript 知道 JSX 的结果是HTMLElement.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

不带 React 的 TypeScript JSX 的相关文章

随机推荐

  • C++: std::tie 与 std::ignore 的返回类型

    我想知道 C 11 标准是否给出了关于type of the std tuple由返回std tie当一些论点是std ignore 更具体地说 我可以假设 decltype std tie 42 std ignore 不等于decltyp
  • 64 位 Microsoft SQL Server 数据工具

    我无法找到 64 位版本的 Microsoft SQL Server Data Tools 有谁知道哪里可以下载64位版本Microsoft SQL Server 数据工具 Visual Studio 2013 和 SQL Server 2
  • 在 Swift 中检查 iOS 设备是否有 LiDAR

    Swift 有没有办法检查设备是否有 LiDAR 传感器 不幸的是 我在苹果官方纪录片和互联网搜索中都没有找到任何信息 我当前的解决方法是确定设备类型 如本文中所述 如何确定当前的iPhone 设备型号 https stackoverflo
  • Sencha Touch 2 - 无法从控制器获取参考视图

    我正在尝试通过参考查看 在控制器中定义的方法内 我收到错误消息 对象 object Object 没有方法 getUserForm 这是我的视图和控制器 如果有人可以帮助我 我会很高兴 提前致谢 用户表格 Ext define appNam
  • .htaccess url重写

    我想重写一个URL 假设您访问http files domain com uploads file name jpg http files domain com uploads file name jpg 我如何使用 htaccess 来编
  • Python - 导入函数和本地声明函数之间的性能差异?

    在 Python 中导入函数与在当前文件中声明函数在性能上是否存在显着差异 我有一个小函数 单行 我经常在几个函数中使用它 py我的程序中的文件 我希望只定义一次 以便我对它所做的更改反映在各处 但是 我不确定将它用作导入函数是否会在调用它
  • 使 (Mac)Vim 重新打开并在关闭时打开文件

    使用案例 MacVim 窗口中显示 2 3 个文件 您按 ctrl Q MacVim 就会关闭 当您重新启动 MacVim 时 它会打开并显示与您关闭它时相同的文件 你怎么能这么做呢 ANSWER 我将以下内容添加到我的 vmirc sav
  • 组合 Spark UDF(而不是将 UDF 作为一个)时是否会造成性能损失?

    我想知道编写 Spark udf 是否会降低性能 一般来说 我更喜欢编写只做一件事的小函数 这是一个简单的例子 给定一个DataFrame df def inc udf i Double gt i 1 def double udf i Do
  • Gson:java.text.ParseException:无法解析的日期:“2018-04-09T09:00:00 + 02:00”

    如何解析以下格式的字符串日期 2018 04 09T09 00 00 02 00 Gson使用 new SimpleDateFormat yyyy MM dd T HH mm ss Z Locale US 但它给出了以下例外 com goo
  • Python:为什么要腌制?

    我一直在使用pickle并且很高兴 然后我看到了这篇文章 不要腌制你的数据 http www benfrederickson com 2014 02 12 dont pickle your data html 进一步阅读似乎是 泡菜很慢 h
  • 如何中止存储桶的所有不完整分段上传

    有时 分段上传会因某种原因挂起或无法完成 在这种情况下 您将陷入难以删除的孤立部分 您可以通过以下方式列出它们 aws s3api list multipart uploads bucket BUCKETNAME 我正在寻找方法来中止它们
  • 如何更改过滤器中 http 响应的正文

    我正在尝试使用过滤器来检查响应正文中的 HTML 标记 问题是 如果我改变过滤器中的主体 当它到达客户端时它不会改变 我尝试了此处显示的解决方案 寻找使用 servlet 过滤器将内容插入响应的示例 https stackoverflow
  • Laravel 5:如何将播种器类添加到自动加载?

    我遵循文档 http laravel com docs master migrations database seeding http laravel com docs master migrations database seeding
  • 按 eloquent 中的最佳匹配排序

    我从雄辩的查询中得到了一些结果 我想按最佳匹配对它们进行排序 我怎样才能在 laravel eloquent 中做到这一点 在这里我找到了一些 SQL 解决方案 但我无法在 eloquent 构建器中使用它 SELECT TOP 5 FRO
  • 使用 ogr2ogr 将 svg 转换为 geojson 失败

    我从以下位置下载了芬兰的 svg 地图http www amcharts com svg maps map finland http www amcharts com svg maps map finland 我想将其转换为 topojso
  • 无法在 VSCode 中使用 pygame

    我目前正在做一项任务 我必须完成一些功能并导入 pygame 以便我可以运行一个跑步程序 当我尝试通过运行命令导入 pygame 时pip3 install r requirements txt 有一个需求文本文件 里面只写着 pygame
  • Azure Service Fabric 节点、节点类型、实例和规模集

    在尝试了几天Azure的Service Fabric之后 我仍然对以下四个关键词感到不舒服 实例 节点 节点类型 规模设定 他们的意思是什么 有什么区别 Instance 取决于上下文 它可能意味着虚拟机 服务实例等 Node 集群内的节点
  • Moment.js 如何使用 fromNow() 在几小时内返回所有内容?

    我已经搜索过moment js 文档 http momentjs com docs and 堆栈溢出 https stackoverflow com 的一种使用方法fromNow 功能但在几小时内返回所有内容 我的意思是 moment 20
  • android mms通过mms url下载mms内容

    我正在尝试下载MMS通过 MMS url 获取图片内容 但返回 403 禁止 服务器响应 其中包含无效内容MSISDN数字 我已将我的代码粘贴在下面以供参考 提前致谢 private static boolean downloadThrou
  • 不带 React 的 TypeScript JSX

    我想在 TypeScript 中使用 JSX 语法 但不想使用 React 我在这里看到了其他相关问题的答案 但没有任何内容足够完整或详细 无法提供任何帮助 我读了本指南 https basarat gitbooks io typescri