本篇是**「一起学习** TypeScript **」**系列的开篇之作,接下来笔者将持续深耕,不定时更新一系列精彩纷呈的TypeScript文章,旨在解答使用TypeScript过程中的各种疑难杂症,一起探索TS的无穷魅力!
一、引言
在进入正题之前,我们先来明确一个概念,什么是"类型安全"(Type Safety)?
看看ChatGPT怎么回答这个问题:
"Type safety"(类型安全)是指编程语言的一个特性,它确保在编译时或运行时,程序中的类型匹配是正确的。这意味着程序员不能将一个类型不匹配的值赋给一个变量,或者将一个类型不匹配的参数传递给一个函数。这可以避免许多常见的编程错误,例如将字符串传递给一个期望整数的函数,或者将一个浮点数赋给一个期望布尔值的变量。类型安全还可以提高程序的可读性和可维护性,因为它使得程序员更容易理解代码中的数据类型和操作。
一句话总结下:类型安全是指确保数据在使用时保持预期的类型。
二、可以实现子组件 "类型安全"么?
如果你已经使用过TS和JSX一段时间,也许你会面临一种情形:想要将一个组件的子组件限制为某种特定的类型。
那么,这个述求能否实现呢?🤔
先来看一个简单的例子🌰:现在有一个Select
组件,它的子组件为Option
。如果想确保Option是唯一能够传递给Select的组件👇🏻
javascript
//✅
<Select>
<Option value="1">One</Option>
<Option value="2">Two</Option>
<Option value="3">Three</Option>
</Select>
//❌
<Select>
<div>One</div>
<div>Two</div>
<div>Three</div>
</Select>
看起来似乎有很多选择:使用ReactElement
,或者ReactNode
,或者ReactChild
?
实际上,这些类型都没有任何效果,你不能告诉 TypeScript"只使用指定类型的组件作为子组件"!
组件总是JSX.Element类型
根本原因在于 TypeScript 解释 JSX 的方式:不管是什么组件,组件里面有什么 props , TS 看到一个JSX语句时,总是把它定义为一个 JSX.Element
!
当我们想让Select
组件仅接收Option
组件作为参数时,我们也许会这么写:
ini
type SelectProps = {
children: ReactElement<OptionProps>[];
}
但是,实际上,Option
组件还是会返回一个JSX.Element
!🤯
尝试手动覆盖返回类型
那么,如果我们使用TS的as
语法覆盖组件的返回类型呢?
下面的代码👇🏻中,你会发现element的类型依然是React.JSX.Element
!😒
不过,如果你手动调用Option() ,确实会达到想要的效果:
这种手动调用的方式其实是绕过了JSX解释的机制,所以TS能够理解Option
要返回的是我们指定的字符串。
但是需要注意的是,在React中手动调用Option()
是一个糟糕的主意------它会打破React对代码所做的各种假设,将会导致一些不可预计的错误。-_-||
三、结论
综上所述: 在 TypeScript 中不可能将 React 组件的子组件限制为某种类型 。
但是,这里仍然有一件事情需要思考:你是真的想要限制子组件类型吗?😏
React的魔力本质上是在于你可以按照你喜欢的任何方式组合你的组件。但是如果你限制了子组件为一种固定的类型,你将打破这种可组合性(composability) !
一种更好的方式是:也许你可以通过使用对子组件的props进行限制来实现你的目的👇🏻
ini
<Select
options={[
{ value: "1", label: "One" },
{ value: "2", label: "Two" },
{ value: "3", label: "Three" },
]}
/>
使用上面这种方式,你仍然可以限制children的类型,但是不会打破组件的可组合性 😉