函数、数组、对象的类型

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-29

函数的类型

需要注意的是,在 TypeScript 的函数类型定义中, => 左边表示是输入类型,需要用括号括起来,右边是输出类型,与ES6箭头函数中的 => 不是同一个概念。

用接口定义函数的形状

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

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

可选参数

? 表示可选的参数。需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了。

function buildName(firstName: string, lastName?: string) {
  if (lastName) {
    return firstName + ' ' + lastName;
  } else {
    return firstName;
  }
}

let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

参数默认值

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

剩余参数

... 的方式获取函数中的剩余参数。

function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
  });
};

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

...rest 中的 rest 是一个数组,由所有的剩余参数组成,rest参数只能是最后一个参数

重载

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

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

// 为什么不直接使用最后一个联合类型的写法呢,要加上两个重载呢
// 因为这样有一个缺点,就是不能精准的表达,可能会理解为 输入数字也能返回数字或字符串

TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义卸载前面。

数组的类型

数组类型有以下表示方式:

let fibonacci: number[] = [1, 1, 2, 3, 5];

let fibonacci: Array<number> = [1, 1, 2, 3, 5];

用接口表示数组

interface NumberArray {
  [index: number]: number;
  // 索引的类型为数字时,值的类型也得是数字
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

类数组

比如 argument 这个类数组,应该使用接口来表示类数组

function sum() {
  let args: number[] = arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.”

// 正确的用法 应该使用接口来表示
function sum() {
  let args: {
    [index: number]: number;
    length: number;
    callee: Function;
  } = arguments;
}

// 或直接使用已有的类数组定义 比如 IArguments、NodeList、HTMLCollection
function sum() {
  let args: IArguments = arguments;
}

// 而实际上 TypeScript帮我们定义的 IArguments 就是这样写的
interface IArguments {
  [index: number]: number;
  length: number;
  callee: Function;
}

使用typeof取出数组中的某一项作为类型

const list = [
  {
    name: 'jack',
    age: 18
  },
  {
    name: 'andy',
    age: 20,
    email: 'xxx'
  }
]

function handleListItem(item: (typeof list)[number]) {
  console.log(item)
}

对象的类型

TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对对象的形状进行描述。

interface Person {
  name: string;
  age: number;
}

let tom: Person = {
  name: 'Tom',
  age: 25
}

定义一个变量tom的类型为Person,也就约束了tom的形状必须和接口Person一致(少一些属性或多一些属性都是不允许的)。

可选属性

interface Person {
  name: string;
  age?: number;
}

let tom: Person = {
  name: 'Tom',
  age: 25
}

let jerry: Person = {
  name: 'jerry',
}

任意属性

interface Person {
  name: string;
  age?: number;
  [propName: string]: any;
}

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

// 错误
interface Person {
  name: string;
  age?: number;
  [propName: string]: string;

  // error 类型“number | undefined”的属性“age”不能赋给字符串索引类型“string”。
}

这里的索引属性就是任意属性,允许是 string,但是可选属性的类型却是 number,而 number 不是 string 的子集,

在一个接口中,只能定义一个任意属性,如果接口中有多个类型的属性,可以在任意属性中使用联合类型。

// 正确
interface Person {
  name: string;
  age?: number;
  [propName: string]: string | number | undefined;
  // 可选属性 等同于  age: number | undefined
}

只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以使用 readonly 定义只读属性:

interface Person {
  readonly id: number;
  name: string;
  age?: number;
}

let tom: Person = {
  id: 89757,
  name: 'Tom',
};

// error 无法分配到 "id" ,因为它是只读属性。
tom.id = 9527;

同时需要注意的是,只读属性也相当于确定属性,tom 如果是 Person 类,就一定要有 id 属性,且不能改变其值。