接口
接口声明引入新类型。接口是定义代码协定的常见方式。
任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态。
接口通常包含属性和方法的声明
示例:
代码语言:javascript复制interface Style { color: string // 属性}interface AreaSize { calculateAreaSize(): number // 方法的声明 someMethod(): void; // 方法的声明}
实现接口的类示例:
代码语言:javascript复制// 接口:interface AreaSize { calculateAreaSize(): number // 方法的声明 someMethod(): void; // 方法的声明}
// 实现:class RectangleSize implements AreaSize { private width: number = 0 private height: number = 0 someMethod(): void { console.log('someMethod called'); } calculateAreaSize(): number { this.someMethod(); // 调用另一个方法并返回结果 return this.width * this.height; }}
接口属性
接口属性可以是字段、getter、setter或getter和setter组合的形式。
属性字段只是getter/setter对的便捷写法。以下表达方式是等价的:
代码语言:javascript复制interface Style { color: string}
代码语言:javascript复制interface Style { get color(): string set color(x: string)}
实现接口的类也可以使用以下两种方式:
代码语言:javascript复制interface Style { color: string}
class StyledRectangle implements Style { color: string = ''}
代码语言:javascript复制interface Style { color: string}
class StyledRectangle implements Style { private _color: string = '' get color(): string { return this._color; } set color(x: string) { this._color = x; }}
接口继承
接口可以继承其他接口,如下面的示例所示:
代码语言:javascript复制interface Style { color: string}
interface ExtendedStyle extends Style { width: number}
继承接口包含被继承接口的所有属性和方法,还可以添加自己的属性和方法。
泛型类型和函数
泛型类型和函数允许创建的代码在各种类型上运行,而不仅支持单一类型。
泛型类和接口
类和接口可以定义为泛型,将参数添加到类型定义中,如以下示例中的类型参数Element:
代码语言:javascript复制class CustomStack<Element> { public push(e: Element):void { // ... }}
要使用类型CustomStack,必须为每个类型参数指定类型实参:
代码语言:javascript复制let s = new CustomStack<string>();s.push('hello');
编译器在使用泛型类型和函数时会确保类型安全。参见以下示例:
代码语言:javascript复制let s = new CustomStack<string>();s.push(55); // 将会产生编译时错误
泛型约束
泛型类型的类型参数可以绑定。例如,HashMap<Key, Value>容器中的Key类型参数必须具有哈希方法,即它应该是可哈希的。
代码语言:javascript复制interface Hashable { hash(): number}class HasMap<Key extends Hashable, Value> { public set(k: Key, v: Value) { let h = k.hash(); // ...其他代码... }}
在上面的例子中,Key类型扩展了Hashable,Hashable接口的所有方法都可以为key调用。
泛型函数
使用泛型函数可编写更通用的代码。比如返回数组最后一个元素的函数:
代码语言:javascript复制function last(x: number[]): number { return x[x.length - 1];}last([1, 2, 3]); // 3
如果需要为任何数组定义相同的函数,使用类型参数将该函数定义为泛型:
代码语言:javascript复制function last<T>(x: T[]): T { return x[x.length - 1];}
现在,该函数可以与任何数组一起使用。
在函数调用中,类型实参可以显式或隐式设置:
代码语言:javascript复制// 显式设置的类型实参last<string>(['aa', 'bb']);last<number>([1, 2, 3]);
// 隐式设置的类型实参// 编译器根据调用参数的类型来确定类型实参last([1, 2, 3]);
泛型默认值
泛型类型的类型参数可以设置默认值。这样可以不指定实际的类型实参,而只使用泛型类型名称。下面的示例展示了类和函数的这一点。
代码语言:javascript复制class SomeType {}interface Interface <T1 = SomeType> { }class Base <T2 = SomeType> { }class Derived1 extends Base implements Interface { }// Derived1在语义上等价于Derived2class Derived2 extends Base<SomeType> implements Interface<SomeType> { }
function foo<T = number>(): T { // ...}foo();// 此函数在语义上等价于下面的调用foo<number>();
空安全
默认情况下,ArkTS中的所有类型都是不可为空的,因此类型的值不能为空。这类似于TypeScript的严格空值检查模式(strictNullChecks),但规则更严格。
在下面的示例中,所有行都会导致编译时错误:
代码语言:javascript复制let x: number = null; // 编译时错误let y: string = null; // 编译时错误let z: number[] = null; // 编译时错误
可以为空值的变量定义为联合类型T | null。
代码语言:javascript复制let x: number | null = null;x = 1; // okx = null; // okif (x != null) { /* do something */ }
非空断言运算符
后缀运算符!可用于断言其操作数为非空。
应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T:
代码语言:javascript复制class C { value: number | null = 1;}
let c = new C();let y: number;y = c.value 1; // 编译时错误:无法对可空值作做加法y = c.value! 1; // ok,值为2
空值合并运算符
空值合并二元运算符??用于检查左侧表达式的求值是否等于null或者undefined。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。
换句话说,a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。
在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:
代码语言:javascript复制class Person { // ... nick: string | null = null getNick(): string { return this.nick ?? ''; }}
可选链
在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。
代码语言:javascript复制class Person { nick: string | null = null spouse?: Person
setSpouse(spouse: Person): void { this.spouse = spouse; }
getSpouseNick(): string | null | undefined { return this.spouse?.nick; }
constructor(nick: string) { this.nick = nick; this.spouse = undefined; }}
说明:getSpouseNick的返回类型必须为string | null | undefined,因为该方法可能返回null或者undefined。
可选链可以任意长,可以包含任意数量的?.运算符。
在以下示例中,如果一个Person的实例有不为空的spouse属性,且spouse有不为空的nickname属性,则输出spouse.nick。否则,输出undefined:
代码语言:javascript复制class Person { nick: string | null = null spouse?: Person
constructor(nick: string) { this.nick = nick; this.spouse = undefined; }}
let p: Person = new Person('Alice');p.spouse?.nick; // undefined
---
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!