TypeScript手册


二、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会做隐式类型转换,像 0NaN""0nnull 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'});

文章作者: ycs
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ycs !
  目录