二、TypeScript 基础语法入门
2.9类型注解和类型推断
2.9.1 类型注解
什么是类型注解,类型注解就是人工的告诉TS,变量或者对象的明确属性类型
const userName:string='23'
2.9.2 类型推断
如果类型推断能够自动推断出来的类型就没有手写类型注解
const userNick='Dell';
总结
假如类型推断不符合要求,那么我们将对代码进行类型注解。
2.10 类型收窄
将类型推导为更精准的类型的过程,我们称之为收窄
function padLeft(padding:number|string,input:string){
if(typeof padding==="number"){
return new Array(padding+1).join(" ")+input;
}
return padding + input;
}
2.10.1 typeof类型保护
在ts中,检查typeof返回值就是一种类型保护。ts知道typeof不同值的结果,它也能识别JavaScript中一些怪异的地方
function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return new Array(padding + 1).join(" ") + input
}
return padding + input;
}
2.10.2 真值收窄
if语句不需要条件的结果总是布尔类型,这是因为javaScript会做隐式类型转换,像 0
、NaN
、""
、0n
、null
undefined
这些值都会被转为 false
,其他的值则会被转为 true
。
也可以使用 Boolean
函数强制转为 boolean
值,或者使用更加简短的!!
function multiplyAll(
values:number[]|undefined,
factor:number
): number[] | undefined{
if(!values){
return values
}
return values.map(x=>x*factor)
}
2.10.3 等值收窄
typescript 也会使用 switch
语句和等值检查比如 ==
!==
==
!=
去收窄类型
function example(x:string|number,y:string|boolean){
if(x===y){
// 当两值相等时,两值均为string类型
x.toLowerCase();
y.toLowerCase();
}else{
console.log(x);
console.log(y);
}
}
判断具体的字面量值也能让 TypeScript 正确的判断类型。
function printAll(strs:string| string[] | null){
if(strs!==null){
if(typeof strs ==="object"){
for(const s of strs){
console.log(s);
}
}
}else if(typeof strs==="string"){
console.log(strs);
}
}
此时上述的例子中,就可以正常处理strs
的值为空字符串的情况。
JavaScript 的宽松相等操作符如 ==
和 !=
也可以正确的收窄。
不过在 JavaScript 中,通过 == null
这种方式并不能准确的判断出这个值就是 null
,它也有可能是 undefined
。对 == undefined
也是一样。
所以我觉得在以后的代码直接用===
严格进行条件判断就ok了。
2.10.4 in操作符收窄
JavaScript 中有一个 in
操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
// (parameter) animal: Fish
}
return animal.fly();
// (parameter) animal: Bird
}
2.10.5 instanceof 收窄
instanceof
也是一种类型保护,TypeScript 也可以通过识别 instanceof
正确的类型收窄
function logValue(x:Date|string){
if(x instanceof Date){
console.log(x.toUTCString());
}else{
console.log(x.toUpperCase());
}
}
2.10.6 赋值语句(Assignments)
TypeScript 可以根据赋值语句的右值,正确的收窄左值。
// 这里将x推导为string | numner
let x=Math.random()<0.5?10:'叶春锁';
// 所以下面我们不管将其赋值为string类型数据还是number类型数据,这都是可行的
x=1;
console.log(x);
x="哈哈哈";
console.log(x)
2.10.7 控制流分析(Control flow analysis)
function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return new Array(padding + 1).join(" ") + input;
}
return padding + input;
}
在第一个 if
语句里,因为有 return
语句,TypeScript 就能通过代码分析,判断出在剩余的部分 return padding + input
,如果 padding 是 number
类型,是无法达到 (unreachable) 这里的,所以在剩余的部分,就会将 number
类型从 number | string
类型中删除掉。
这种基于可达性(reachability) 的代码分析就叫做控制流分析(control flow analysis)。
function example(){
let x:string|number|boolean;
x=Math.random()<0.5;
// 这里x推导为boolean
console.log(x);
if(Math.random()<0.5){
x="hello";
// 这里x是string类型
console.log(x);
}else{
x=100;
// 这里x是number
console.log(x);
}
// 所以返回值将是string|number 类型。
return x;
}
2.10.8 类型谓词(type predicates)
所谓 predicate
就是一个返回 boolean
值的函数。
type Fish = { swim: () => void };
type Bird = { fly: () => void };
// pet is Fish 就是类型谓词
// 格式为parameterName is Type
// 其中parameterName 必须是当前函数的参数名
function isFish(pet: Fish | Bird): pet is Fish {
return 'swim' in pet;
}
2.10.9 类型谓词(type predicates)
当联合类型中的每个类型,都包含了一个共同的字面量类型的属性,TypeScript 就会认为这是一个可辨别联合(discriminated union),然后可以将具体成员的类型进行收窄。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
2.10.9 可辨别联合(Discriminated unions)
当联合类型中的每个类型,都包含了一个共同的字面量类型的属性,TypeScript 就会认为这是一个可辨别联合(discriminated union),然后可以将具体成员的类型进行收窄。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
在这个例子中,kind
就是这个公共的属性(作为 Shape 的可辨别(discriminant) 属性 )
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
} else {
return shape.sideLength ** 2
}
}
2.10.10 never 类型
当进行收窄的时候,如果你把所有可能的类型都穷尽了,TypeScript 会使用一个 never
类型来表示一个不可能存在的状态。
穷尽检查(Exhaustiveness checking)
除了 never
自身,没有类型可以赋值给 never
。这就意味着你可以在 switch
语句中使用 never
来做一个穷尽检查。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
// 只有当Shape的所有类型都被处理完毕
// default分支中的shape的类型才会是never
// 当我们给 Shape 类型添加一个新成员,却没有做对应处理的时候,就会导致一个 TypeScript 错误
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
2.11 对象相关知识
对象类型解构的代码怎么写
function getObjectValue({a,b}:{a:string,b:string}){
return a + b;
}
getObjectValue({a:'1',b:'2'});