轉(zhuǎn)發(fā) TypeScript基礎(chǔ)入門之JSX(二)
屬性類型檢查
鍵入檢查屬性的第一步是確定元素屬性類型。
內(nèi)在元素和基于價值的元素之間略有不同。
對于內(nèi)部元素,它是JSX.IntrinsicElements上的屬性類型
declare namespace JSX {
interface IntrinsicElements {
foo: { bar?: boolean }
}
}
// element attributes type for 'foo' is '{bar?: boolean}'
<foo bar />;
對于基于價值的元素,它有點(diǎn)復(fù)雜。
它由先前確定的元素實(shí)例類型上的屬性類型確定。
使用哪個屬性由JSX.ElementAttributesProperty確定。
它應(yīng)該用單個屬性聲明。
然后使用該屬性的名稱。
從TypeScript 2.8開始,如果未提供JSX.ElementAttributesProperty,則將使用類元素的構(gòu)造函數(shù)或SFC調(diào)用的第一個參數(shù)的類型。
declare namespace JSX {
interface ElementAttributesProperty {
props; // specify the property name to use
}
}
class MyComponent {
// specify the property on the element instance type
props: {
foo?: string;
}
}
// element attributes type for 'MyComponent' is '{foo?: string}'
<MyComponent foo="bar" />
元素屬性類型用于鍵入檢查JSX中的屬性。
支持可選和必需的屬性。
declare namespace JSX {
interface IntrinsicElements {
foo: { requiredProp: string; optionalProp?: number }
}
}
<foo requiredProp="bar" />; // ok
<foo requiredProp="bar" optionalProp={0} />; // ok
<foo />; // error, requiredProp is missing
<foo requiredProp={0} />; // error, requiredProp should be a string
<foo requiredProp="bar" unknownProp />; // error, unknownProp does not exist
<foo requiredProp="bar" some-unknown-prop />; // ok, because 'some-unknown-prop' is not a valid identifier
注意:如果屬性名稱不是有效的JS標(biāo)識符(如data-*屬性),則如果在元素屬性類型中找不到它,則不會將其視為錯誤。
此外,JSX.IntrinsicAttributes接口可用于指定JSX框架使用的額外屬性,這些屬性通常不被組件的props或參數(shù)使用 - 例如React中的鍵。
進(jìn)一步說,通用JSX.IntrinsicClassAttributes <T>類型也可用于為類組件(而不是SFC)指定相同類型的額外屬性。
在此類型中,泛型參數(shù)對應(yīng)于類實(shí)例類型。
在React中,這用于允許類型為Ref <T>的ref屬性。
一般來說,這些接口上的所有屬性都應(yīng)該是可選的,除非您打算讓JSX框架的用戶需要在每個標(biāo)記上提供一些屬性。
擴(kuò)展操作符也是有效的:
var props = { requiredProp: "bar" };
<foo {...props} />; // ok
var badProps = {};
<foo {...badProps} />; // error
子類型檢查
在TypeScript 2.3中,TS引入了子類型檢查。
children是元素屬性類型中的特殊屬性,其中子JSXExpressions被插入到屬性中。
類似于TS使用JSX.ElementAttributesProperty來確定props的名稱,TS使用JSX.ElementChildrenAttribute來確定這些props中的子項(xiàng)名稱。
應(yīng)使用單個屬性聲明JSX.ElementChildrenAttribute。
declare namespace JSX {
interface ElementChildrenAttribute {
children: {}; // specify children name to use
}
}
<div>
<h1>Hello</h1>
</div>;
<div>
<h1>Hello</h1>
World
</div>;
const CustomComp = (props) => <div>props.children</div>
<CustomComp>
<div>Hello World</div>
{"This is just a JS expression..." + 1000}
</CustomComp>
您可以像任何其他屬性一樣指定子類型。
這將覆蓋默認(rèn)類型,例如React typings(如果您使用它們)。
interface PropsType {
children: JSX.Element
name: string
}
class Component extends React.Component<PropsType, {}> {
render() {
return (
<h2>
{this.props.children}
</h2>
)
}
}
// OK
<Component>
<h1>Hello World</h1>
</Component>
// Error: children is of type JSX.Element not array of JSX.Element
<Component>
<h1>Hello World</h1>
<h2>Hello World</h2>
</Component>
// Error: children is of type JSX.Element not array of JSX.Element or string.
<Component>
<h1>Hello</h1>
World
</Component>
JSX結(jié)果類型
默認(rèn)情況下,JSX表達(dá)式的結(jié)果鍵入為any。您可以通過指定JSX.Element接口來自定義類型。但是,無法從此接口檢索有關(guān)JSX的元素,屬性或子級的類型信息。這是一個黑盒子。
嵌入表達(dá)式
JSX允許您通過用大括號({})包圍表達(dá)式來在標(biāo)記之間嵌入表達(dá)式。
var a = <div>
{["foo", "bar"].map(i => <span>{i / 2}</span>)}
</div>
上面的代碼將導(dǎo)致錯誤,因?yàn)槟荒軐⒆址詳?shù)字。
使用preserve選項(xiàng)時,輸出如下所示:
var a = <div>
{["foo", "bar"].map(function (i) { return <span>{i / 2}</span>; })}
</div>
React整合
要將JSX與React一起使用,您應(yīng)該使用React類型。
這些類型適當(dāng)?shù)囟x了JSX名稱空間以與React一起使用。
/// <reference path="react.d.ts" />
interface Props {
foo: string;
}
class MyComponent extends React.Component<Props, {}> {
render() {
return <span>{this.props.foo}</span>
}
}
<MyComponent foo="bar" />; // ok
<MyComponent foo={0} />; // error
工廠函數(shù)
jsx:react編譯器選項(xiàng)使用的確切工廠函數(shù)是可配置的。
可以使用jsxFactory命令行選項(xiàng)或內(nèi)聯(lián)@jsx注釋編譯指示來設(shè)置它以基于每個文件進(jìn)行設(shè)置。
例如,如果將jsxFactory設(shè)置為createElement,則<div />將作為createElement("div")而不是React.createElement("div")來編譯。
注釋pragma版本可以像這樣使用(在TypeScript 2.8中):
import preact = require("preact");
/* @jsx preact.h */
const x = <div />;
編譯為
const preact = require("preact");
const x = preact.h("div", null);
選擇的工廠也將影響JSX命名空間的查找位置(用于類型檢查信息),然后再回退到全局命名空間。
如果工廠定義為React.createElement(默認(rèn)值),編譯器將在檢查全局JSX之前檢查React.JSX。
如果工廠定義為h,它將在全局JSX之前檢查h.JSX。