前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JSX_TypeScript笔记17

JSX_TypeScript笔记17

作者头像
ayqy贾杰
发布于 2019-06-12 07:17:34
发布于 2019-06-12 07:17:34
2.4K00
代码可运行
举报
文章被收录于专栏:黯羽轻扬黯羽轻扬
运行总次数:0
代码可运行

一.基本用法

TypeScript 也支持JSX,除了能够像Babel一样把 JSX 编译成 JavaScript 外,还提供了类型检查

只需 2 步,即可使用 TypeScript 写 JSX:

  • 源码文件用.tsx扩展名
  • 开启--jsx选项

此外,TypeScript 提供了 3 种 JSX 处理模式,分别对应不同的代码生成规则:

Mode

Input

Output

Output File Extension

preserve

<div />

<div />

.jsx

react

<div />

React.createElement("div")

.js

react-native

<div />

<div />

.js

也就是说:

  • preserve:生成.jsx文件,但保留 JSX 语法不转换,交给后续构建环节(如Babel)处理
  • react:生成.js文件,将 JSX 语法转换成React.createElement
  • react-native:生成.js文件,但保留 JSX 语法不转换

这些模式通过--jsx选项来指定,默认"preserve",只影响代码生成,并不影响类型检查(例如--jsx "preserve"要求不转换,但仍会对 JSX 进行类型检查)

具体使用上,JSX 语法完全保持一致,唯一需要注意的是类型断言

类型断言

在 JSX 中只能用as type(尖括号语法与 JSX 语法冲突)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let someValue: any = "this is a string";
// <type>
let strLength: number = (<string>someValue).length;

.tsx文件中会引发报错:

JSX element ‘string’ has no corresponding closing tag. ‘</’ expected.ts

由于语法冲突,<string>someValue中的类型断言部分(<string>)被当成 JSX 元素了。所以在.tsx中只能使用as type形式的类型断言:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// as type
let strLength: number = (someValue as string).length;

P.S.关于 TypeScript 类型断言的更多信息,见三.类型断言

二.元素类型

对于一个 JSX 表达式<expr />expr可以是环境中的固有元素(intrinsic element,即内置组件,比如 DOM 环境中的divspan),也可以是基于值的元素(value-based element),即自定义组件。两种元素的区别在于:

  • 生成的目标代码不同 React 中,固有元素会生成字符串(比如React.createElement("div")),而自定义组件不会(比如React.createElement(MyComponent)
  • 元素属性(即Props)类型的查找方式不同 固有元素的属性是已知的,而自定义组件可能想要指定自己的属性集

形式上,要求自定义组件必须首字母大写,以此区分两种 JSX 元素

P.S.实际上,固有元素/基于值的元素与内置组件/自定义组件说的是一回事,对 TypeScript 编译器而言,内置组件的类型已知,称之为固有元素,自定义组件的类型与组件声明(值)有关,称之为基于值的元素

固有元素

固有元素的类型从JSX.IntrinsicElements接口上查找,如果没有声明该接口,那么所有固有元素都不做类型检查,如果声明了,就在JSX.IntrinsicElements上查找对应的属性,作为类型检查的依据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
declare namespace JSX {
  interface IntrinsicElements {
    foo: any
  }
}

// 正确
<foo />;
// 错误 Property 'bar' does not exist on type 'JSX.IntrinsicElements'.
<bar />;

当然,也可以配合索引签名允许使用未知的内置组件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
declare namespace JSX {
  interface IntrinsicElements {
    foo: any;
    [elemName: string]: any;
  }
}

// 正确
<bar />;

好处是将来扩展支持新内置组件后,不需要立即修改类型声明,代价是失去了白名单的严格校验

基于值的元素

基于值的元素直接从作用域里找对应标识符,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import MyComponent from "./myComponent";

// 正确
<MyComponent />
// 错误 Cannot find name 'SomeOtherComponent'.
<SomeOtherComponent />

共有 2 种基于值的元素:

  • 无状态的函数式组件(Stateless Functional Component,所谓 SFC)
  • 类组件(Class Component)

二者单从 JSX 表达式的形式上区分不开,因此先当作 SFC 按照函数重载去尝试解析,解析失败才当类组件处理,还失败就报错

无状态的函数式组件

形式上是个普通函数,要求第一个参数是props对象,返回类型是JSX.Element(或其子类型),例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Welcome(props: { name: string }) {
  return <h1>Hello, {props.name}</h1>;
}

同样地,函数重载仍然适用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Welcome(props: { content: JSX.Element[] | JSX.Element });
function Welcome(props: { name: string });
function Welcome(props: any) {
  <h1>Hello, {props.name}</h1>;
}

<div>
  <Welcome name="Lily" />
  <Welcome content={<span>Hello</span>} />
</div>

P.S.JSX.Element类型声明来自@types/react

类组件

类组件则继承自React.Component,与 JavaScript 版没什么区别:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class WelcomeClass extends React.Component {
  render() {
    return <h1>Hello, there.</h1>;
  }
}

<WelcomeClass />

类似于 Class 的双重类型含义,对于 JSX 表达式<Expr />,类组件的类型分为 2 部分:

  • 元素类类型(element class type):Expr的类型,即typeof WelcomeClass
  • 元素实例类型(element instance type):Expr类实例的类型,即{ render: () => JSX.Element }

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 元素类类型
let elementClassType: typeof WelcomeClass;
new elementClassType();
// 元素实例类型
let elementInstanceType: WelcomeClass;
elementInstanceType.render();

要求元素实例类型必须是JSX.ElementClass的子类型,默认JSX.ElementClass类型为{},在 React 里则限定必须具有render方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace JSX {
  interface ElementClass extends React.Component<any> {
    render(): React.ReactNode;
  }
}

(摘自DefinitelyTyped/types/react/index.d.ts)

否则报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class NotAValidComponent {}
function NotAValidFactoryFunction() {
  return {};
}

<div>
  {/* 错误 JSX element type 'NotAValidComponent' is not a constructor function for JSX elements. */}
  <NotAValidComponent />
  {/* 错误 JSX element type '{}' is not a constructor function for JSX elements. */}
  <NotAValidFactoryFunction />
</div>

三.属性类型

属性检查首先要确定元素属性类型(element attributes type),固有元素和基于值的元素在属性类型上存在些许差异:

  • 固有元素的属性类型:JSX.IntrinsicElements上对应属性的类型
  • 基于值的元素属性类型:元素实例类型上特定属性类型上对应属性的类型,这个特定属性通过JSX.ElementAttributesProperty指定

P.S.如果未声明JSX.ElementAttributesProperty,就取组件类构造函数或 SFC 第一个参数的类型

具体的,固有元素属性以ahref为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace JSX {
  interface IntrinsicElements {
    // 声明各个固有元素,及其属性类型
    a: {
      download?: any;
      href?: string;
      hrefLang?: string;
      media?: string;
      rel?: string;
      target?: string;
      type?: string;
      referrerPolicy?: string;
    }
  }
}

// 元素属性类型为 { href?: string }
<a href="">链接</a>

基于值的元素属性例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace JSX {
  // 指定特定属性名为 props
  interface ElementAttributesProperty { props: {}; }
}

class MyComponent extends React.Component {
  // 声明属性类型
  props: {
    foo?: string;
  }
}
// 元素属性类型为 { foo?: string }
<MyComponent foo="bar" />

可选属性、展开运算符等也同样适用,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MyComponent extends React.Component {
  // 声明属性类型
  props: {
    requiredProp: string;
    optionalProp?: string;
  }
}

const props = { optionalProp: 'optional' };
// 正确
<MyComponent { ...props } requiredProp="required" />

P.S.另外,JSX 框架可以通过JSX.IntrinsicAttributes指定框架所需的额外属性,比如 React 里的key,具体见Attribute type checking

P.S.特殊的,属性校验只针对属性名为合法 JavaScript 标识符的属性data-*之类的不做校验

子组件类型检查

子组件的类型来自元素属性类型上的children属性,类似于用ElementAttributesProperty指定props,这里用JSX.ElementChildrenAttribute来指定children

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace JSX {
  // 指定特定属性名为 children
  interface ElementChildrenAttribute { children: {}; }
}

const Wrapper = (props) => (
  <div>
    {props.children}
  </div>
);
<Wrapper>
  <div>Hello World</div>
  {"This is just a JS expression..." + 1000}
</Wrapper>

children指定类型的方式与普通属性类似:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface PropsType {
  children: JSX.Element
  name: string
}
class Component extends React.Component<PropsType, {}> {
  render() {
    return (
      <h2>
        {this.props.children}
      </h2>
    )
  }
}

<Component name="hello">
  <h1>Hello World</h1>
</Component>

子组件类型不匹配会报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 错误 Type '{ children: Element[]; name: string; }' is not assignable to type 'Readonly<PropsType>'.
<Component name="hello">
  <h1>Hello World</h1>
  <h1>Hi</h1>
</Component>

四.结果类型

默认情况下,一个 JSX 表达式的结果类型是any

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// a 的类型为 any
let a = <a href="" />;
a = {};

可以通过JSX.Element来指定,例如 React 中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let a = <a href="" />;
// 错误 Type '{}' is missing the following properties from type 'Element': type, props, key.
a = {};

对应的类型声明类似于:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace JSX {
  interface Element<T, P> {
    type: T;
    props: P;
    key: string | number | null;
  }
}

P.S.React 里具体的 JSX 元素类型声明见DefinitelyTyped/types/react/index.d.ts

五.嵌入的表达式

JSX 允许在标签内通过花括号语法({ })插入表达式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

(摘自Embedding Expressions in JSX)

TypeScript 同样支持,并且能够对嵌入的表达式做类型检查:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const a = <div>
  {/* 错误 The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. */}
  {["foo", "bar"].map(i => <span>{i / 2}</span>)}
</div>

六.结合 React

引入React 类型定义之后,很容易描述 Props 的类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface WelcomeProps {
  name: string;
}
// 将 Props 的类型作为第一个类型参数传入
class WelcomeClass extends React.Component<WelcomeProps, {}> {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
// 错误 Property 'name' is missing in type '{}' but required in type 'Readonly<WelcomeProps>'.
let errorCase = <WelcomeClass />;
let correctCase = <WelcomeClass name="Lily" />;

P.S.关于类型参数及泛型的更多信息,见二.类型变量

工厂函数

React 模式(--jsx react)下,可以配置具体使用的 JSX 元素工厂方法,有 2 种方式:

  • --jsxFactory选项:项目级配置
  • 内联@jsx注释指令:文件级配置

默认为--jsxFactory "React.createElement",将 JSX 标签转换为工厂方法调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const div = <div />;
// 编译结果
var div = React.createElement("div", null);

在Preact里对应的 JSX 元素工厂方法为h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* @jsx preact.h */
import * as preact from "preact";
<div />;
// 或者
/* @jsx h */
import { h } from "preact";
<div />;

P.S.注意,@jsx注释指令必须出现在文件首行,其余位置无效

编译结果分别为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* @jsx preact.h */
var preact = require("preact");
preact.h("div", null);
// 或者
/* @jsx h */
var preact_1 = require("preact");
preact_1.h("div", null);

P.S.另外,工厂方法配置还会影响 JSX 命名空间的查找,比如默认--jsxFactory React.createElement的话,优先查找React.JSX,接下来才看全局JSX命名空间,如果指定--jsxFactory h,就优先查找h.JSX

七.总结

TypeScript 中 JSX 的类型支持分为元素类型、属性类型和结果类型 3 部分,如下图:

参考资料

  • JSX
  • TypeScript 2.8: Per-File JSX Factories
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端向后 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
MySQL是如何保证不丢数据的(二)
上篇文章我们聊了单机模式下,MySQL是如何保证数据一致性的,但是在实际的生产环境中,很少采用单机模式。现在所有的集群架构都是从MySQL的主从复制演变过来的。MySQL的主从复制是通过将主库的binlog发送至从库,从库重新提交主库的变更来实现主从数据的一致性。MySQL的主从复制主要分为三种:异步复制、半同步复制、组复制(MGR)。
MySQL数据库技术栈
2020/08/05
2.5K0
MySQL是如何保证不丢数据的(二)
MySQL高可用架构-MMM、MHA、MGR、PXC
半同步复制在提交过程中增加了一个延迟:提交事务时,在客户端接收到查询结束反馈前必须保证二进制日志已经传输到一台备库上。
用户4283147
2022/10/27
2K0
MySQL高可用架构-MMM、MHA、MGR、PXC
大厂都在用的MySQL主从复制、读写分离及高可用方案
随着数据量的增大,读写并发的增加,系统可用性要求的提升,单机 MySQL 出现危机:
JavaEdge
2021/02/20
8.3K0
MySQL集群架构[通俗易懂]
题记: 文章内容输出来源:拉勾教育Java高薪训练营。 本篇文章是 MySQL 学习课程中的一部分笔记。
全栈程序员站长
2022/09/18
1.5K0
MySQL集群架构[通俗易懂]
数据库高可用架构设计,看这篇就够了!!!
又赶上一年一度的金九银十的日子,这段期间的招聘岗位相对前几个月会多些,如果在目前公司没有进步、没有前途时,这段时间可以准备一下,去外面看看机会。不过在外面找工作时,可以提前在网上看看招聘信息,看看自己是否达到公司要求。如果多看下高薪资的技术人员招聘要求时,就会发现对三高都有一定的要求,比如下面一家公司的要求就对高并发、高负载和高可用性系统设计要有开发经验。
一个会写诗的程序员
2023/03/08
2.8K0
数据库高可用架构设计,看这篇就够了!!!
MySQL-主从架构探索
http://www.searchdoc.cn/rdbms/mysql/dev.mysql.com/doc/refman/5.7/en/index.com.coder114.cn.html
小小工匠
2021/08/17
3910
mysql学习之mysql集群
​ mysql主从架构部署比较简单,常见架构根据主从节点个数不同分成 一主多从,多主一从,双主节点等。
全栈程序员站长
2022/11/01
3.1K0
mysql学习之mysql集群
面试系列-mysql主从复制
MySQL主从复制涉及到三个线程,一个运行在主节点(log dump thread),其余两个(I/O thread, SQL thread)运行在从节点:
用户4283147
2022/10/27
1.3K0
面试系列-mysql主从复制
MySQL高可用架构探秘:主从复制剖析、切换策略、延迟优化与架构选型
在分布式系统中,单机节点在发生故障时无法提供服务,这可能导致长期的服务不可用,从而影响其他节点的运作,导致的后果非常严重
菜菜的后端私房菜
2024/06/28
5770
MySQL高可用架构案例篇:UCloud最佳实践
内容来源:2017年7月22日,UCloud高级研发工程师王松磊在“饿了么技术沙龙【第九弹】上海研发中心·运维专场”进行《数据库高可用架构》演讲分享。IT 大咖说(微信id:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。 阅读字数:3280 | 9分钟阅读 摘要 分享UCloud在数据库高可用上的最佳实践。首先介绍MYSQL常见的高可用方式,并分析其存在的问题,然后给出UCloud对此的思考和解决方法。 嘉宾演讲视频及PPT回顾:http://suo.im/2obXuQ MySQL
IT大咖说
2018/06/04
7840
技术分享 | 无损半同步复制下,主从高可用切换后数据一致吗?
MySQL5.7 默认参数下我们开启了半同步,在一个事务提交(commit) 的过程时,在 MySQL 层的 write binlog 步骤后,Master 节点需要收到至少一个 Slave 节点回复的 ACK (表示收到了binlog )后,才能继续下一个事务;
爱可生开源社区
2022/08/25
7570
美团点评MySQL数据库高可用架构从MMM到MHA+Zebra以及MHA+Proxy的演进
本文介绍最近几年美团点评MySQL数据库高可用架构的演进过程,以及我们在开源技术基础上做的一些创新。同时,也和业界其它方案进行综合对比,了解业界在高可用方面的进展,和未来我们的一些规划和展望。 MMM
庞小明
2018/03/09
3.9K0
美团点评MySQL数据库高可用架构从MMM到MHA+Zebra以及MHA+Proxy的演进
基于MHA搭建MySQL Replication集群高可用架构
MHA是Master High Availability的缩写,它是目前MySQL高可用方面的一个相对成熟的解决方案,其核心是使用perl语言编写的一组脚本,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
端碗吹水
2020/09/23
1.1K0
基于MHA搭建MySQL Replication集群高可用架构
爱奇艺 MySQL 高可用方案到底有多牛?
爱奇艺每天都为数以亿计的用户提供7x24小时不间断的视频服务。通过爱奇艺的平台,用户可以方便的获取海量、优质、高清的视频资源。但如果服务平台出现故障,会有大量的用户将无法正常播放视频,因此我们的应用服务以及数据库服务都必须具备高可用架构。
开发者技术前线
2020/11/23
1.1K0
爱奇艺 MySQL 高可用方案到底有多牛?
一文搞懂MySQL主从复制方案、读写分离及高可用
随着数据量的增大,读写并发的增加,系统可用性要求的提升,单机 MySQL 出现危机:
JavaEdge
2021/02/03
1.3K0
一文搞懂MySQL主从复制方案、读写分离及高可用
MySQL 主从架构原理
上图展示的是 MySQL 的主从切换流程。在 State-1 中,客户端的读写都直接访问节点 A,而节点 B 是 A 的备库,只是将 A 的更新都同步过来,到本地执行。这样可以保持节点 B 和 A 的数据是相同的。当需要切换的时候,就切成状态 2。这时候客户端读写访问的都是节点 B,而节点 A 是 B 的从库。
张申傲
2020/09/03
1.2K0
MySQL 主从架构原理
MYSQL高可用架构MMM实现
描述: MySQL的MMM(Master-Master replication manager for MySQL)是一套 支持双主故障切换和双主日常管理的脚本程序高可用架构;
全栈工程师修炼指南
2022/09/28
1.1K0
MYSQL高可用架构MMM实现
深入浅出MySQL复制--MySQL的"核心科技"
MySQL复制是MySQL成功的最重要原因之一,前东家某公司内网上有相关资料,低下评论戏称"核心科技",今天将核心科技分享给大家
DBA札记
2023/02/15
4970
深入浅出MySQL复制--MySQL的"核心科技"
高可用架构-- MySQL主从复制的配置
环境 操作系统:CentOS-6.6-x86_64-bin-DVD1.iso MySQL版本:mysql-5.6.26.tar.gz 主节点IP:192.168.1.205 主机名:edu-mysql-01 从节点IP:192.168.1.206 主机名:edu-mysql-02 主机配置:4核CPU、4G内存 依赖课程 《高可用架构篇--第13节--MySQL源码编译安装(CentOS-6.6+MySQL-5.6)》 MySQL主从复制官方文档 http://dev.mysql.com/doc/refma
思梦php
2018/03/09
1.7K0
高可用架构-- MySQL主从复制的配置
MySQL集群高可用架构之MHA
记得之前发过一篇文章,名字叫《浅析MySQL高可用架构》,之后一直有很多小伙伴在公众号后台或其它渠道问我,何时有相关的深入配置管理文章出来,因此,民工哥,也将对前面的各类架构逐一进行整理,然后发布出来。那么今天将来发布的MHA的架构整体规划与配置操作。
民工哥
2020/09/16
8170
MySQL集群高可用架构之MHA
推荐阅读
相关推荐
MySQL是如何保证不丢数据的(二)
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 一.基本用法
    • 类型断言
  • 二.元素类型
    • 固有元素
    • 基于值的元素
      • 无状态的函数式组件
      • 类组件
  • 三.属性类型
    • 子组件类型检查
  • 四.结果类型
  • 五.嵌入的表达式
  • 六.结合 React
    • 工厂函数
  • 七.总结
    • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档