深入函数:声明、参数、返回值

深入对象语法

描述对象的属性

使用接口描述对象

ts
type Person = {
  name: string
}

// 或者
interface Person {
  name: string
}

Person 的子集为

ts
type Children =
  | { name: string, age: string }
  | { name: string, age: number }
  | { name: string, age: boolean }
  | { name: string, age: null }
  | { name: string, age: undefined }
  | { name: string, age: object }
  | { name: string, gender: string }
  | { name: string, gender: number }
// 以下省略一万行

索引签名(Index Signature)

ts
type Hash = {
  [k: string]: unknown
  length: number
}
type List = {
  [k: number]: unknown
  length: number
}

映射类型(Mapped Type)

ts
type Hash = {
  [k in string]: unknown
}
type List = {
  [k in number]: unknown
}

映射类型多用于泛型

ts
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

需要注意的是,如果使用了映射类型,对象中就不能再有其他属性了,这点与索引签名有所不同

ts
type Hash = {
  [k in string]: unknown
  length: number
  // ^-- A mapped type may not declare properties or methods.
}

可选属性

可选属性的含义是该属性可以不存在

ts
interface InputProps {
  defaultValue?: string;
  value?: boolean;
  onChange?: () => void;
}

value?: boolean 可近似的理解成 value: boolean | undefined,但是在声明对象属性时,value? 可以不写,而 value 必须写

只读属性

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

ts
interface User {
  readonly id: number;
  readonly name: string;
  readonly scores: number[];
  age?: number;
}
const u: User = {
  id: 1,
  name: 'ClariS',
  scores: [87, 65],
};

使用 readonly 定义的属性初始化后不能被重新赋值

ts
u.id = 2;
// ^-- Cannot assign to 'id' because it is a read-only property.ts
u.scores = [100, 65]
// ^-- Cannot assign to 'scores' because it is a read-only property.ts

但是并没有对 scores 内部的属性做只读约束(只读约束只在第一层)

ts
u.scores[0] = 100;
// 不报错

深入函数语法

对象的语法全都适用于函数

ts
type F = {
  (a: number, b: number): number;
  readonly count?: number[];
};

const fn: F = (x, y) => x + y;
fn.count[0] = 1;

声明函数的四种方式

先写类型再赋值

ts
type F1 = (a: number, b: number) => number;
const f1: F1 = (a, b) => a + b;

箭头函数

ts
const f2 = (a: number, b: number): number => {
  return a + b;
};
type F2 = typeof f2;

普通函数

ts
function f3(this: unknown, a: number, b: number): number {
  return a + b;
}
type F3 = typeof f3;

匿名普通函数

ts
const f4 = function (this: unknown, a: number, b: number): number {
  return a + b;
};
type F4 = typeof f4;

构造函数

ts
const f5 = new Function('a', 'b', 'return a + b');
type F5 = typeof f5;

几乎没人会这么写吧 (思考)

类型谓词

之前在联合类型中讲过,可以使用类型谓词 is 来收窄类型,当时只简单提了一下不推荐使用箭头函数,这里说一下为什么

ts
type Person = { name: string }
type Animal = {}

function isPerson(x: Person | Animal): x is Person {
  return 'name' in x
}

function fn(a: Person | Animal) {
  if (isPerson(a)) {
    a // Person
  }
}

可以使用箭头函数实现 isPerson

ts
const isPerson = (x: Person | Animal): x is Person => 'name' in x

但需要注意的是,如果使用的是箭头函数,不能先写类型再赋值

ts
type A = (x: Person | Animal) => x is Person
const isPerson: A = x => 'name' in x
//       ^--- Type '(x: Person | Animal) => boolean' is not assignable to type 'A'

在右边加上 x is Person 就不报错了

ts
type A = (x: Person | Animal) => x is Person
const isPerson: A = (x): x is Person => 'name' in x

参数相关语法

可选参数

ts
function addEventListener(eventType: string, fn: unknown, useCapture?: boolean) {
  if (useCapture === undefined) {
    useCapture = false
  }
  // 浏览器实现
  console.log(eventType, fn, useCapture)
}

addEventListener('click', () => 1)

参数默认值

ts
function addEventListener(eventType: string, fn: unknown, useCapture = false) {
  // 浏览器实现
  console.log(eventType, fn, useCapture)
}

addEventListener('click', () => 1)

参数也可以是函数

ts
function addEventListener(
  eventType: string,
  fn: (this: HTMLElement, e: Event) => void,
  useCapture = false
) {
  // 浏览器实现,这里只提供伪代码
  const element = {} as HTMLElement;
  const event = {} as Event;
  fn.call(element, event);
  console.log(eventType, fn, useCapture);
}

addEventListener('click', () => 1);

返回值也可以是函数

ts
const add = (a: number, b: number) => a + b
type CreateAdd = (x: number) => (y: number) => number
const createAdd: CreateAdd = a => b => a + b

add(6, 14)
createAdd(6)(14)
深入函数:函数重载、this 和 as const
类型兼容与赋值

评论区

评论加载中...