一. 如何使用tsx
- webpack配合create-react-app,Vue Cli
- vite选择tsx模板配合vite的ts插件,@vitejs/plugin-vue-jsx
- 如果后台要用tsx,next.js/remix.js/fresh.js
二. tsx中标签与断言的冲突
const header = <h1>hi</hi>
const a = 1 as unknown
const b = a as number
const c = <number>a // 不要再tsx中出现这种断言,会有冲突
二. JSX/TSX的本质
const header = <h1 name="frank">hi</h1>
// 编译成
import {create} from 'react_or_vue/jsx'
const header = create('h1', { name: 'frank' }, 'hi')
// 这么写的类型是什么?
- 需要写上类型才能使用
const header = <h1 name="frank">hi</h1>;
// 通过这种方式定义和扩展tsx的类型
declare global {
namespace JSX {
interface IntrinsicElements {
name?: string;
age?: number;
enabled: boolean;
}
}
}
- declare global { ... }: 这是一个全局声明,它告诉 TypeScript 在全局范围内添加一些额外的类型定义。
- namespace JSX { ... }: 这里创建了一个 JSX 的命名空间,用于定义 JSX 元素的类型。
- interface IntrinsicElements { ... }: 在 JSX 命名空间中,定义了一个名为 IntrinsicElements 的接口。这个接口用于定义 JSX 元素的类型信息。
- { name?: string; age?: number; enabled: boolean; }: 在 IntrinsicElements 接口中定义了一个对象类型,其中包含了三个属性:
- name?: string;:一个可选的字符串属性,表示 JSX 元素可以具有一个名为 "name" 的属性,其值是字符串类型。
- age?: number;:一个可选的数字属性,表示 JSX 元素可以具有一个名为 "age" 的属性,其值是数字类型。
- enabled: boolean;:一个必需的布尔属性,表示 JSX 元素必须具有一个名为 "enabled" 的属性,其值是布尔类型。
三. jsx.element 是什么
const header = <h1 name="frank">hi</h1>;
// 通过这种方式定义和扩展tsx的类型
declare global {
// 我们创建的标签类型是由jsx.element指定的
interface Element {
tag: string;
}
namespace JSX {
interface IntrinsicElements {
name?: string;
age?: number;
enabled: boolean;
}
}
}
- 总结如下图所示
1.png
四. 函数组件
- 函数组件的props,不是由IntrinsicElements的value指定,而是由函数组件的第一个参数决定
const header = <h1 enabled>hi</h1>;
const Header = (props: { level: number }, context: unknown) => {
return <h1>我的等级是{props.level}</h1>;
};
// 如果用的是函数组件,props就不是由IntrinsicElements的value指定,而是由函数组件的第一个参数决定
const App = <Header level={123}></Header>;
declare global {
interface Element {
tag: string;
}
namespace JSX {
interface IntrinsicElements {
name?: string;
age?: number;
enabled: boolean;
}
}
}
五. 类组件
declare global {
namespace JSX {
interface Element {
tag: string;
}
interface IntrinsicElements {
name?: string;
age?: number;
enabled: boolean;
}
}
}
六. 组件共有属性
- IntrinsicAttributes表示每一个标签都有的属性
declare global {
namespace JSX {
interface Element {
tag: string;
}
// 函数和类都有
interface IntrinsicAttributes {
key: string;
}
// 原生有的属性
interface IntrinsicElements {
h1: {
name?: string;
age?: number;
enabled: boolean;
} & IntrinsicAttributes; // 这样写之后h1页必须有key
}
}
}
- IntrinsicClassAttributes表示固有的class属性
class ClassHeader {
props: {
level: number;
};
constructor(props: { level: number }) {
this.props = props;
}
render() {
return <h1>level</h1>;
}
}s
const App2 = (
<ClassHeader level={123} key="abc" ref={{ current: null }}></ClassHeader>
);
declare global {
namespace JSX {
interface Element {
tag: string;
}
// 函数和类都有
interface IntrinsicAttributes {
key: string;
}
// 原生有的属性
interface IntrinsicElements {
h1: {
name?: string;
age?: number;
enabled: boolean;
} & IntrinsicAttributes; // 这样写之后h1页必须有key
}
interface IntrinsicClassAttributes<T> {
ref: {
current: T | null;
};
}
}
}
七. jsx如何把内容变成props的属性
const Header = (
// children来控制标签中间的内容
props: { level: number; children: number },
context: unknown
) => {
return <h1>我的等级是{props.level}</h1>;
};
const App = <Header level={123}>{123}</Header>;
declare global {
namespace JSX {
interface ElementChildrenAttribute {
children: {};
}
...
}
}
八. React与Vue源码中的JSX声明
- 在项目中写上type X = JSX.Element,之后点JSX进去查看源文件,react和vue都可以
九. JSX.Element VS ReactElement VS ReactNode
- JSX.Element就是ReactElement
- ReactNode 包含了ReactElement等元素
import { ReactElement, ReactNode } from 'react';
type X = JSX.Element; // JSX.Element继承了ReactElement
type A = ReactElement;
type B = ReactNode; // 这个范围更大具体根据定义有以下这些
// type ReactNode =
// | ReactElement
// | string
// | number
// | Iterable<ReactNode>
// | ReactPortal
// | boolean
// | null
// | undefined
// | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES];
九. React 事件处理函数的类型怎么写?
- 总结: 先点以下原有的类型,然后拷过来,有泛型就传泛型
import { ChangeEventHandler, MouseEventHandler } from 'react';
const App2 = () => {
const onClick: MouseEventHandler<HTMLInputElement> | undefined = (e) => {
console.log(e.currentTarget.value);
console.log((e.target as HTMLInputElement).value);
};
const onChange: ChangeEventHandler<HTMLInputElement> | undefined = (e) => {
console.log(e.target.value);
};
// onchange 触发时机是在失去焦点时
// oninput 触发时机是在输入时
// oncompositionstart 开始输入
// oncompositionend 结束输入
return (
<input onClick={onClick} onChange={onChange}>
hi
</input>
);
};
十. 如何指定children的类型?
- 总结: Children能够指定很容易区分的东西,比如数组,string等,但是做不到是B组件创建的Div还是C组件的Div,这只能用js做检查
type AProps = {
children?: ReturnType<typeof B>;
};
type BProps = {};
type CProps = {};
const A: React.FC<AProps> = (props) => {
return <div>{props.children}</div>;
};
const B: React.FC<BProps> = (props) => <div>B组件</div>;
const C: React.FC<CProps> = (props) => <div>B组件</div>;
const App = (
<A>
{/* 没有办法指定children是函数组件,因为returnType就是ReactElement */}
<B></B>
<C></C>
</A>
);
- 用js做判断
type AProps = {
children?: React.ReactElement<typeof B>;
};
type BProps = {};
type CProps = {};
const B: React.FC<BProps> = (props) => <div>B组件</div>;
const C: React.FC<CProps> = (props) => <div>B组件</div>;
const A: React.FC<AProps> = (props) => {
if (props.children?.type !== B) {
throw new Error('children必须是B组件');
}
return <div>{props.children}</div>;
};
十一. React泛型组件是什么
- 可以参考这篇文章,mp.weixin.qq.com/s/LAXPCJr7Z…
const App = () => <Show<string> content="hi" onClick={(c) => console.log(c)} />;
interface Props<T> {
content: T;
onClick: (arg: T) => void;
}
function Show<T>(props: Props<T>) {
const { content, onClick } = props;
return (
<div>
{content as React.ReactNode}
<button onClick={() => onClick(content)}>Click me</button>
</div>
);
}
- ts中只传泛型
function f<T>(x: T) {
return null
}
// 注意这里f要用括号括起来
const f2 = (f)<string>
f2('hi')
- 之前的代码可以写成
const App = () => {
const X = (Show)<string>
return (
<X content="hi" onClick={(c) => console.log(c)} >
)
}
interface Props<T> {
content: T;
onClick: (arg: T) => void;
}
function Show<T>(props: Props<T>) {
const { content, onClick } = props;
return (
<div>
{content as React.ReactNode}
<button onClick={() => onClick(content)}>Click me</button>
</div>
);
}
- 总结: 只要两个属性有强关联,就可以声明泛型组件。这个组件接收一个泛型,这个泛型和props相关联,至少和两个props的属性关联