可选链
可选链不是ts专属,自ts3.7版本后内置,它确实解决了对象属性或方法访问前判断是否存在的痛点,可选链更直观
来自mdn的解释
可选链操作符?.能够去读取一个被连接对象的深层次的属性的值而无需明确校验链条上每一个引用的有效性。?.运算符功能类似于.运算符,不同之处在于如果链条上的一个引用是null 或 undefined,.操作符会引起一个错误,?.操作符取而代之的是会按照短路计算的方式返回一个undefined。当?.操作符用于函数调用时,如果该函数不存在也将会返回undefined。 当访问链条上可能存在的属性却不存在时,?.操作符将会使表达式更短和更简单。当不能保证哪些属性是必需的时,?.操作符对于探索一个对象的内容是很有帮助的。
interface Person {
name: string;
age: number;
say: (str: string) => string;
}
const p: Person = {
name: "tom",
age: 18,
say(str: string) {
return str;
}
};
p?.name;
p?.age;
p?.say("hello girl")
//p?.log() //报错 log方法不存在
//看下编译后结果吧
"use strict";
var p = {
name: "tom",
age: 18,
say: function (str) {
return str;
}
};
p === null || p === void 0 ? void 0 : p.name;
p === null || p === void 0 ? void 0 : p.age;
p === null || p === void 0 ? void 0 : p.say("hello girl");
//# sourceMappingURL=index.js.map
类型兼容性
ts的类型兼容是基于结构化类型的,简单来说:如果x要兼容y,那么y至少具有与x相同的属性。换言之,具有公共属性,就具备了类型兼容的条件。
1. 对象的类型兼容性
//来看个有意思的例子
interface Animal{
name:string
}
interface Person{
name:string
}
let a:Animal={
name:"panfish"
};
let p:Person={
name:"tom"
}
//注意这里,两种赋值都不报错
//ts的结构化类型,某种程度上panfish和tom等价
p=a;
a=p;
// 接下来在上边的例子中做一下改动,Person加一个age属性
interface Person{
name:string
age:number
}
//这里也加一条属性
let p:Person={
name:"tom",
age:18
}
//其他不变
p=a;//报错,结构化类型角度考虑,a中没有age属性,可多不可少
a=p;//不报错
2. 函数的类型兼容性
函数也是对象,同样适用类型兼容性规则,但只关注参数类型和顺序而不关注参数名
let fn1 = (m: number): number => m;
let fn2 = (n: number, s: string): number => +s+n;
//这里好理解
console.log(fn1(100));//100
console.log(fn2(100,"1"));//101
// 赋值--注意:返回值不一样肯定是不兼容的
fn2=fn1;//ok
fn1=fn2;//报错
// 如果调换f2参数顺序为 s: string,n: number,以下皆报错
fn2=fn1;//报错
fn1=fn2;//报错
3. 枚举的类型兼容性
枚举类型与数字类型双向兼容,基于字符串的枚举不兼容,跨类型的枚举类型之间不兼容。
3.1 基于数字的枚举
enum Time {
moring,
noon,
night
}
let moring = Time.moring;
let num:number = 10;
//这里很容易理解,因为 Time.moring拿到的值就是一个数字类型
moring=num;//ok
num=moring//ok
3.2 跨类型枚举
enum Language{
java,
js,
node,
php,
python
}
//跨类型的枚举类型之间不兼容
let js=Language.js;
js=moring;//error
moring=js;//error
4 类的类型兼容性
类的类型兼容性大体和对象那部分的相似,但要注意的是:类有静态部分和实例部分的类型。静态属性和静态方法以及构造函数会在结构化类型兼容过程中被忽略,只比较实例属性和实例方法。
public 级别的类型兼容性
//实际比较的是实例属性name,实例方法say
class Animal {
//实例属性
name: string;
//构造函数
constructor(name: string) {
this.name = name;
}
//实例方法
say():void{
}
//静态方法
static say():void{
console.log("静态方法 say")
}
//静态属性
static flag: string = "animal";
}
//实例属性name,age 实例方法say
class Person {
//实例属性
name: string;
age:number;
constructor(name: string,age:number) {
this.name = name;
this.age=age;
}
say():void{
}
}
let a: Animal = {
name: "cat",
say:()=>{
console.log("say animal")
}
};
let p: Person = {
name: "tom",
age:18,
say:()=>{
console.log("say person")
}
};
a.say()//say animal
p.say();//say person
a = p;//ok
p = a;//error a比p少一个age属性,无法兼容
private/protected 级别的类型兼容性
不同于public级别的类型兼容性,private/protected 要求更严格,必须来自同一个类
//protected 和 private类似
class Cat{
//即使一方属性私有化,另一方是public也不行
private name:string;
constructor(name:string){
this.name=name;
}
}
class Animal{
name:string;
constructor(name:string){
this.name=name;
}
}
let c:Cat={
name:"cat"
};
let a:Animal={
name:"animal"
}
c=a;//error
a=c;//error
参考链接
- 掘金ts小册:https://juejin.im/book/5da08714518825520e6bb810
- ts中文手册:https://zhongsp.gitbooks.io/typescript-handbook
- ts入门教程:https://ts.xcatliu.com
- ts常见问题整理:https://juejin.im/post/5e33fcd06fb9a02fc767c427
- mdn可选链:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE