Typescript常用类型(任意值any、数组Array、函数Function、元组Tuple、类型推论、联合类型)

系列文章目录

引入一:Typescript基础引入(基础类型、元组、枚举)
引入二:Typescript面向对象引入(接口、类、多态、重写、抽象类、访问修饰符)
第一章:Typescript基础知识(Typescript介绍、搭建TypeScript环境、基本数据类型)
第二章:Typescript常用类型(任意值any、数组Array、函数Function、元组Tuple、类型推论、联合类型)
第三章:Typescript基础知识(类型断言、类型别名、字符串字面量类型、枚举、交叉类型)
第四章:Typescript基础知识(类型拓宽、类型缩小)
第五章:TypeScript进阶知识之类(类的定义、类的基本使用、类的构造函数、类的属性和方法、访问修饰符、类的继承、抽象类)
第六章:TypeScript进阶知识之接口(接口定义、接口属性、可索引类型、接口表示函数类型、额外的属性检查、接口继承、接口与类型别名的区别)
第七章:TypeScript进阶知识之泛型(泛型的定义、为什么要使用泛型、泛型的使用、泛型变量、多个类型参数、泛型类、泛型接口、泛型参数默认类型、泛型约束)



1.任意值any

1.1 什么是任意值类型

  • 在 TypeScript 中,任何类型都可以被归为 any 类型。当你不希望某个特定值导致类型检查错误时,可以使用它。这让any类型成为了类型系统的顶级类型。
  • 当一个值的类型是any时,可以访问它的任何属性,将它分配给任何类型的值,或者几乎任何其他语法上的东西都合法的
  • 但如果是 any 类型,则允许被赋值为任意类型:

    let a: any = 666;
    a = "Semlinker";
    a = false;
    a = 66
    a = undefined
    a = null
    a = []
    a = {}
    

1.2 任意值的属性和方法

  • 在任意值上访问任何属性都是允许的:

    let anyThing: any = 'hello';
    console.log(anyThing.myName);
    console.log(anyThing.myName.firstName);
    
  • 也允许调用任何方法:

    let anyThing: any = 'Tom';
    anyThing.setName('Jerry');
    anyThing.setName('Jerry').sayHello();
    anyThing.myName.setFirstName('Cat');
    

    可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值

1.3 未声明类型的变量

  • 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:

    let something;
    something = 'seven';
    something = 7;
    
    something.setName('Tom');
    

    等价于

    let something: any;
    something = 'seven';
    something = 7;
    
    something.setName('Tom');
    

    使用 any 类型,可以很容易地编写类型正确但在运行时有问题的代码。如果我们使用 any 类型,就无法使用 TypeScript 提供的大量的保护机制。
    为了解决 any 带来的问题,TypeScript 3.0 引入了 unknown 类型。

2.数组Array

2.1 对数组类型的定义两种方式

  • 最简单的方法是使用「类型 + 方括号」来表示数组:

    let fibonacci: number[] = [1, 1, 2, 3, 5];
    
  • 我们也可以使用数组泛型Array 来表示数组:

    let fibonacci: Array<number> = [1, 1, 2, 3, 5];
    
  • 接口也可以用来描述数组:

    // interface是接口,后面会讲到
    interface Arrobj{
        name:string,
        age:number
    }
    let arr3:Arrobj[]=[{name:'jimmy',age:22}]
    
    interface NumberArray {
        [index: number]: number;
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5];
    

    NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。

  • 定义联合类型数组:

    let arr:(number | string)[];
    // 表示定义了一个名称叫做arr的数组, 
    // 这个数组中将来既可以存储数值类型的数据, 也可以存储字符串类型的数据
    arr3 = [1, 'b', 2, 'c'];
    

2.2 any 在数组中的应用

一个比较常见的做法是,用 any 表示数组中允许出现任意类型:

let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

3.函数Function

3.1 函数声明

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

function sum(x: number, y: number): number {
  return x + y;
}

注意: 输入多余的(或者少于要求的)参数,是不被允许的

3.2 函数表达式

  • 写一个对函数表达式的定义:

    let mySum = function (x: number, y: number): number {
    	return x + y;
    };
    

    上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的mySum,是通过赋值操作进行类型推论而推断出来的。

  • 手动给 mySum 添加类型,则应该是这样:

    let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    	  return x + y;
    };
    

    在 TypeScript 的类型定义中,=> 用来表示函数的定义左边是输入类型,需要用括号括起来,右边是输出类型

3.3 用接口定义函数的形状

我们也可以使用接口的方式来定义一个函数需要符合的形状:

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

注意: 采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。

3.4 可选参数

前面提到,输入多余的(或者少于要求的)参数,是不允许的。

与接口中的可选属性类似,我们用 ? 表示可选的参数

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

注意: 可选参数必须接在必需参数后面。可选参数后面不允许再出现必需参数了

3.5 参数默认值

TypeScript 会将添加了默认值的参数识别为可选参数:

此时就不受「可选参数必须接在必需参数后面」的限制了

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');

3.6 剩余参数

使用 …rest 的方式获取函数中的剩余参数(rest 参数):
items 是一个数组。所以我们可以用数组的类型来定义它

function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

3.7 函数重载

函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。 要解决前面遇到的问题,方法就是为同一个函数提供多个函数类型定义来进行函数重载,编译器会根据这个列表去处理函数的调用。

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理

案例一:
我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 ‘hello’ 的时候,输出反转的字符串 ‘olleh’

function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

然而这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。

我们可以使用重载定义多个 reverse 的函数类型:

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。

案例二:

我们希望 add 函数同时支持 string 和 number 类型,因此我们可以定义一个 string | number 联合类型,同时我们为该联合类型取个别名:

type Combinable = string | number;

在定义完 Combinable 联合类型后,我们来更新一下 add 函数:

function add(a: Combinable, b: Combinable) {
    if (typeof a === 'string' || typeof b === 'string') {
     return a.toString() + b.toString();
    }
    return a + b;
}

为 add 函数的参数显式设置类型之后,之前错误的提示消息就消失了。但这时 TypeScript 编译器又出现以下错误信息了:

const result = add('Semlinker', ' Kakuqo');
result.split(' ');
//Property 'split' does not exist on type 'number'.

很明显 number 类型的对象上并不存在 split 属性。问题又来了,那如何解决呢?这时我们就可以利用 TypeScript 提供的函数重载特性。

type Types = number | string
function add(a:number,b:number):number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a:Types, b:Types) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}
const result = add('Semlinker', ' Kakuqo');
result.split(' ');

在以上代码中,我们为 add 函数提供了多个函数类型定义,从而实现函数的重载。

4.元组Tuple

4.1元组的定义

  • 数组一般由同种类型的值组成,但有时我们需要在单个变量中存储不同类型的值,这时我们就可以使用元组。元组是 TypeScript中特有的类型,工作方式类似于数组。
  • 元组最重要的特性是可以限制数组元素的个数和类型,适合用来实现多值返回。
  • 元组用于保存定长定数据类型的数据
let x: [string, number]; 
// 类型必须匹配且个数必须为2

x = ['hello', 10]; // OK 
x = ['hello', 10,10]; // Error 
x = [10, 'hello']; // Error

4.2 元祖类型的解构赋值

通过下标的方式来访问元组中的元素,当元组中的元素较多时,这种方式并不是那么便捷。此时可以使用元组的解构赋值

let employee: [number, string] = [1, "Semlinker"];
let [id, username] = employee;
console.log(`id: ${id}`);//id: 1
console.log(`username: ${username}`);//username: Semlinker

注意,在解构赋值时,如果解构数组元素的个数是不能超过元组中元素的个数,否则也会出现错误,比如:

let employee: [number, string] = [1, "Semlinker"];\
let [id, username, age] = employee;
//Tuple type '[number, string]' of length '2' has no element at index '2'.

很明显元组类型 [number, string] 的长度是 2,在位置索引 2 处不存在任何元素。

4.3 元组类型的可选元素

与函数签名类型,在定义元组类型时,我们也可以通过 ? 号来声明元组类型的可选元素,具体的示例如下:

let optionalTuple: [string, boolean?];
optionalTuple = ["Semlinker", true];
console.log(`optionalTuple : ${optionalTuple}`);//optionalTuple : Semlinker,true
optionalTuple = ["Kakuqo"];
console.log(`optionalTuple : ${optionalTuple}`);//optionalTuple : Kakuqo

4.4 元组类型的剩余元素

元组类型里最后一个元素可以是剩余元素,形式为 …X,这里 X 是数组类型。剩余元素代表元组类型是开放的,可以有零个或多个额外的元素。 例如,[number, …string[]] 表示带有一个 number 元素和任意数量string 类型元素的元组类型。

type RestTupleType = [number, ...string[]];
let restTuple: RestTupleType = [666, "Semlinker", "Kakuqo", "Lolo"];
console.log(restTuple);///[ 666, 'Semlinker', 'Kakuqo', 'Lolo' ]

4.5 只读的元组类型

我们可以为任何元组类型加上 readonly 关键字前缀,以使其成为只读元组,修改只读元组中元素的操作都会抛出异常

const point: readonly [number, number] = [10, 20];

point[0] = 1;// Cannot assign to '0' because it is a read-only property.
point.push(0);// Property 'push' does not exist on type 'readonly [number, number]'.
point.pop();// Property 'pop' does not exist on type 'readonly [number, number]'.

5.类型推论

如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

5.1什么是类型推论

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论

以下代码虽然没有指定类型,但是会在编译的时候报错:

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

它等价于:

let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;

// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

在 TypeScript 中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。比如我们能根据 return 语句推断函数返回的类型,如下代码所示:

{
  /** 根据参数的类型,推断出返回值的类型也是 number */
  function add1(a: number, b: number) {
    return a + b;
  }
  const x1= add1(1, 1); // 推断出 x1 的类型也是 number
  
  /** 推断参数 b 的类型是数字或者 undefined,返回值的类型也是数字 */
  function add2(a: number, b = 1) {
    return a + b;
  }
  const x2 = add2(1);
  const x3 = add2(1, '1'); // ts(2345) Argument of type "1" is not assignable to parameter of type 'number | undefined
}

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

6.联合类型

联合类型表示取值可以为多种类型中的一种使用 | 分隔每个类型

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
let myFavoriteNumber: string | number;
myFavoriteNumber = true;

// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
//   Type 'boolean' is not assignable to type 'number'.

上述例子中,允许 myFavoriteNumber 的类型是 string 或者 number,但是不能是其他类型。

6.1访问联合类型的属性或方法

不确定一个联合类型的变量到底是哪个类型时,我们只能访问此联合类型的所有类型里共有的属性或方法

function getLength(something: string | number): number {
    return something.length;
}

// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
//   Property 'length' does not exist on type 'number'.

上例中,length 不是 string 和 number 的共有属性,所以会报错。

访问 string 和 number 的共有属性是没问题的

function getString(something: string | number): string {
    return something.toString();
}

联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型:

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 编译时报错

// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.

上例中,第二行的 myFavoriteNumber 被推断成了 string,访问它的 length 属性不会报错。

而第四行的 myFavoriteNumber 被推断成了 number,访问它的 length 属性时就报错了。

7.空值void

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数

function alertName(): void {
    alert('My name is Tom');
}

声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null(只在 --strictNullChecks 未指定时):

let unusable: void = undefined;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值