前言:
而今小伙伴们对“object htmlspanelement”大概比较关注,姐妹们都想要分析一些“object htmlspanelement”的相关文章。那么小编也在网摘上收集了一些关于“object htmlspanelement””的相关文章,希望看官们能喜欢,小伙伴们一起来了解一下吧!本节介绍声明合并及JSX相关知识,ts中编译器会将同一个名字的两个独立声明合并为单一声明,合并后声明同时拥有原先两个声明的特性,合并时数量不限于两个。
JSX是一种嵌入式的类似XML的语法,ts中支持内嵌、类型检查和将JSX直接编译为JS。
一、声明合并
ts中声明会创建三种实体之一:命名空间、类型和值。创建命名空间的声明会新建一个命名空间,包含了用.符号来访问时使用的名字,创建类型的声明是:用声明的模型创建一个类型并绑定到给定的名字上,创建值的声明会创建在js输出中看到的值。
1. 合并接口
合并接口时会把双方的成员放到一个同名的接口中,接口的非函数成员必须是唯一的,如果两个接口中同时声明了同名的非函数成员编译器则会报错。
interface B{ height: number; width: number;}interface B{ scale: number;}let box: B= {height: 5, width: 6, scale: 10};
对于函数成员,每个同名函数声明都会被当成这个函数的一个重载,同时当接口A与后来的接口A合并时,后面的接口具有更高的优先级:
interface A{ clone(animal: Animal): Animal;}interface A{ clone(animal: Sheep): Sheep;}interface A{ clone(animal: Dog): Dog; clone(animal: Cat): Cat;}
//合并后的接口如下:
interface A{ clone(animal: Dog): Dog; clone(animal: Cat): Cat; clone(animal: Sheep): Sheep; clone(animal: Animal): Animal;}
每组接口中的声明顺序保持不变,但各组接口之间的顺序是后来的接口重载出现在靠前位置。但有一个例外是当出现特殊的函数签名时,如果签名中有一个参数的类型是单一的字符串字面量,如字符串字面量的联合类型,那么它将会被提升到重载列表的最顶端。
interface D{ createElement(tagName: any): Element;}interface D{ createElement(tagName: "div"): HTMLDivElement; createElement(tagName: "span"): HTMLSpanElement;}interface D{ createElement(tagName: string): HTMLElement; createElement(tagName: "canvas"): HTMLCanvasElement;}
//合并后
interface D{ createElement(tagName: "canvas"): HTMLCanvasElement; createElement(tagName: "div"): HTMLDivElement; createElement(tagName: "span"): HTMLSpanElement; createElement(tagName: string): HTMLElement; createElement(tagName: any): Element;}
2. 合并命名空间
同名的命名空间也会进行合并,声明命名空间的时候会创建出命名空间和值,有不同的合并规则。
对于命名空间的合并:模块导出的同名接口进行合并,构成单一命名空间内含合并后的接口。
对于命名空间值的合并:如果当前已存在给定名字的命名空间,那么后来的命名空间的导出成员会被加到已经存在的那个模块中。
声明合并实例:
namespace A{ export class A1{}}namespace A{ export interface L{ leg:number } export class D{}}
合并后:
namespace A{ export interface L{ leg:number } export class A1{} export class D{}}
对于非导出成员合并时,仅在其原有的命名空间中是可见的,即从其它命名空间合并进来的成员无法访问非导出成员,如下:
namespace A{ let s = true; export function a(){ return s; }}namespace A{ export function ab(){ return s;//此处将报错,因为s属性不是在此处定义的,且并没有导出,合并后无法访问未合并且未导出的成员 }}
3. 命名空间与类和函数和枚举类型合并
命名空间可以和其它类型的声明进行合并,只要命名空间的定义符合将要合并类型的定义,合并结果包含两者的声明类型。
4. 合并命名空间和类
命名空间和类的合并规则和命名空间之间合并的规则类似,合并结果是一个类并带有一个内部类,可以使用命名空间为类增加一些静态属性,如下:
class A{ label:A.B;}namespace A{ export class B{}}
上述实例中B类必须导出,除了内部类的模式,也可以创建一个函数后扩展它增加一些属性,ts使用声明合并来达到同样的效果并保证类型安全,如下:
function buildLabel(name: string): string { return buildLabel.prefix + name + buildLabel.suffix;}namespace buildLabel { export let suffix = ""; export let prefix = "Hello, ";}alert(buildLabel("Sam Smith"));
命名空间也可以扩展枚举类型,如下:
enum Color { red = 1, green = 2,blue = 4}namespace Color { export function mixColor(colorName: string) { if (colorName == "yellow") { return Color.red + Color.green; }else if (colorName == "white") { return Color.red + Color.green + Color.blue; }else if (colorName == "magenta") { return Color.red + Color.blue; }else if (colorName == "cyan") { return Color.green + Color.blue; } }}
5. 非法的合并
类和其它的类及变量无法进行合并。
6. 模块扩展
js本身不支持合并,但可以通过给对象的原先添加属性进行对象的扩展,如下:
a.js
export class A{}
b.js
import {A} from “./a”;A.prototype.a = function(f){}
上述实例可以在ts中运行,但是编译器无法知道A.prototype.a的具体内容,可以使用扩展模块告诉编译器对应的信息,如下:
b.ts
import {A} from “./a”declare module “./A”{ interface A{ a(f:(x)=>U):A }}A.prototype.a = function(f){}
c.ts
import {A} from “./a”import “./b”let c:A;c.a(x=>return s);
模块解析名和使用import和export解析模块标识符的方式是一致的,当这些声明被合并时,就像在原始位置被声明了一样,但不能在扩展中声明新的顶级声明,仅可以扩展模块中已经存在的声明。
7. 全局扩展
可以在模块内部添加声明到全局作用域中:
export class A<T>{}declare global{ interface Array<T>{ toA():A<T> }}Array.prototype.toA = function{ return new A();}
全局模块与模块扩展的行为和限制是相同的。
二、JSX
JXS是一种嵌入式类似XML的语法,可以被转换为合法的js,转换的语义是根据不同的实现而定,ts支持内嵌,类型检查和将jsx直接编译为js。
1. 基本使用
使用jsx需要启用jsx选项,并且文件扩展名需为.tsx。ts具有两种txs模式:preserve和react,此处的模式只在代码生成阶段有用,类型检查不受影响,preserve模式生成的代码会保留jsx以便后续的转换操作,输出文件会带有.jsx扩展名,react模式会直接生成js代码,如React.createElement,不需要进行转换,输出的文件扩展名为.js。
可以通过--jxs或jsconfig.json中的选项指定具体的模式。
2. as操作符
类型断言可以使用<>或者as语法,但是在jxs中无法使用<>,只能使用as语法。若用jxs时<>会和类型断言造成混乱,无法区分具体是jxs的代码还是类型断言。
3. 类型检查
jxs中进行类型检查时需要区分固有元素和基于值的元素,两者的区别是固有元素总是以一个小写字母开头,基于值的元素总是以一个大写字母开通,如自定义组件等。因为对于固有元素和基于值的元素会有不同的处理方式:
1. 对于React,固有元素会生成字符串React.createElement(‘div’),但自定义的组件不会生成React.createElement(myComponent)。
2. 传入JSX元素中的属性类型的查找方式不同,固有元素属性本身就支持,但自定义组件需要指定它们具有哪些属性。
4. 固有元素
固有元素使用特殊的接口JSX.IntrinsicElements进行查找,如果这个接口没有指定,则会全部通过,不对固有元素进行类型检查,如果存在,固有元素的名字需要在此接口的属性中查找,如:
declare namespace JSX { interface IntrinsicElements { foo: any }}<foo />; // 正确<bar />; // 错误
5. 基于值的元素
基于值的元素会简单的在它的作用域中按标识符查找,可以限制基于值的元素的类型:
import MyComponent from "./myComponent";<MyComponent />; // 正确<SomeOtherComponent />; // 错误
若现在有<Expr>,元素类的类型为Expr的类型,上述实例中如果MyComponent是ES6的类,则它的类类型就是这个类,如果是个工厂函数,类类型就是函数。
一旦建立起了类类型,示例类型就确定了,为类类型调用签名的返回值与构造签名的联合类型,在ES6类的情况下,实例类型为这个类的实例的类型,如果是工厂函数,实例类型为这个函数返回值类型。
class MyComponent { render() {}}// 使用构造签名var myComponent = new MyComponent(); // 元素类的类型 => MyComponent// 元素实例的类型 => { render: () => void }function MyFactoryFunction() { return { render: () => { } }}// 使用调用签名var myComponent = MyFactoryFunction();// 元素类的类型 => FactoryFunction// 元素实例的类型 => { render: () => void }
元素的类型必须赋值给JSX.ElementClass或抛出一个异常,默认的JSX.ElementClass为{},但可以被扩展用来限制JSX的类型以符合对应的接口。
declare namespace JSX { interface ElementClass { render: any; }}class MyComponent { render() {}}function MyFactoryFunction() { return { render: () => {} }}<MyComponent />; // 正确<MyFactoryFunction />; // 正确class NotAValidComponent {}function NotAValidFactoryFunction() { return {};}<NotAValidComponent />; // 错误<NotAValidFactoryFunction />; // 错误
6. 属性类型检查
属性类型检查时需要确定元素属性类型,和固有元素和基于值的元素之间不同,对于固有元素对应的是JSX.IntrinsicElements属性的类型:
declare namespace JSX { interface IntrinsicElements { foo: { bar?: boolean } }}// `foo`的元素属性类型为`{bar?: boolean}`<foo bar />;
对于值的元素,取决于先前确定的在元素实例类型上的某个属性的类型,具体使用那个属性来确定类型取决于JSX.ElementAttributesProperty。
declare namespace JSX { interface ElementAttributesProperty { props; // 指定用来使用的属性名 }}class MyComponent { // 在元素实例类型上指定属性 props: { foo?: string; }}// `MyComponent`的元素属性类型为`{foo?: string}`<MyComponent foo="bar" />
元素属性类型用于的JSX里进行属性的类型检查,支持可选属性和必选属性。
declare namespace JSX { interface IntrinsicElements { foo: { requiredProp: string; optionalProp?: number } }} <foo requiredProp="bar" />; // 正确<foo requiredProp="bar" optionalProp={0} />; // 正确<foo />; // 错误, 缺少 requiredProp<foo requiredProp={0} />; // 错误, requiredProp 应该是字符串<foo requiredProp="bar" unknownProp />; // 错误, unknownProp 不存在<foo requiredProp="bar" some-unknown-prop />; // 正确, `some-unknown-prop`不是个合法的标识符
若一个属性不是合法的JS标识符,并没有出现在元素属性类型里时不会报错。也可以使用延展操作符:
var props = { requiredProp: 'bar' };<foo {...props} />; // 正确var badProps = {};<foo {...badProps} />; // 错误
7. JSX结果类型
JSX表达式默认的类型为any,可以自定义这个类型,通过JSX.Element接口,但不能够从接口中检索元素,属性或JSX的子元素的类型信息。
8. 嵌入的表达式
JSX允许使用{}标签来内嵌表达式,如下:
var a= <div>{[‘a’,’b’].map(i=><span>{i/2}</span>)}</div>
上述代码中的会有错误,因为不能用数字来除以一个字符串。