【ts】类型体操-操前开胃菜

164 阅读3分钟

image-20220608163235188

[项目地址] (github.com/type-challe…

extends 关键字

继承作用此处不做讨论

extends官方示例

//BasicAddress
interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}

此时如果需要一个新的interface并且与BasicAddress建立联系

// AddressWithUnit
interface AddressWithUnit {
  name?: string;
  unit: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}

这样可以达成目的,但是必须重复声明所有从BasicAddress的属性类型,事实上我们可以拓展BasicAddress的属性,甚至可以顺便在其中增加新的属性

interface AddressWithUnit extends BasicAddress{
	unit:string;
}
```

当然interface也支持同时多个拓展

```typescript
interface Colorful {
  color: string;
}
 
interface Circle {
  radius: number;
}
 
interface ColorfulCircle extends Colorful, Circle {}
 
const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};
```

### 泛型约束

泛型定义常常包括一些限制,如泛型`A`必须包括类型`b`,那么就可以使用`extends`关键字进行限制

```typescript
//error
//Type '{ latitude: number; }' is not assignable to type '{ longitude: //number; }'.
//  Object literal may only specify known properties, and 'latitude' does not exist in type '{ longitude: number; }'.

type Test<T extends {longitude : number}>={
  [P : string] :T;
}

const temp:Test<{longitude : number}>={
  location:{
    latitude:12345
  }
}
//pass

type Test<T extends {longitude : number}>={
  [P : string] :T;
}

const temp:Test<{longitude : number}>={
  location:{
    longitude:12345
  }
 
  }

```



### 条件类型

> [ts类型体操](https://www.typescriptlang.org/play?#code/PQKgUABBAsDMEFoIFEAeBjANgVwCYFNJEETSiAjATwgC0ALfRgOwHMIAKAAQC8HmWAlBADE+AIYBnasPLYAlpgAuCOUyJFhmiAEVs+CYrkB7NVCIBJALYAHTPkv4miiIoYRZC5apQYcBADwAKgA0EACqAHxEET5YePgQAGYATkaWEIEudEYSCYqU1vpZYs5iyQmSEnIsTGLkdi5G4epQMQBqcvgA7hAmEADicooAEtjkAFwQdIqK1hLjwMCKEuh0AHQAVhJrRskswHBgIMBgp6AQAPpX1zfXEACaRtjJEADCRgQQw-jll7f-Fwgx1O+UKEAAspQ0HEAiFwjEALwQMRMSinMDnAH-DL6ZyvSRFLG3IEnOQ2XbOUEJADeKAAjtgxJhQmhCuhnABfJKpdIAck4VIQqyZdlY+mA2EMmAkvJBBQS6AJEggSIA2kRWfh2f5kAymf5IdC-Ph-LyxLyIAAfCC88gW6289C80Jm3kRFm+eKm81Wm1232O50280RUPBDWoNmKHV6zAGqGegKugP+h1Ol0+h1292xY3e+1+guBjNF7NhiNRmOMuOGxMmgzJVRsa1MbCWcg-X3sdhCBExABuRjkuAEoQAYtgmOzjEwc0avQ2m77W+3O9bu72B0OR+PJ9OTKH3WAALrojEgP5Eq4QCfJVydgDKinwc0vV5J5+iEAfdDKCUoTwvBIRg4IYJjzFMMxzAsSwrOsWw7HsBywMAKISF0Pxfh03QQMBoEzhB0yzPMizLKsmzbLs+xwMAeGSgRX7grsCSvL+mCiiw+iTER0GkXBFGISwRwnGAQA)

```typescript
//type challenge
/*
  43 - Exclude
  -------
  by Zheeeng (@zheeeng) #easy #built-in
  
  ### Question
  
  Implement the built-in Exclude<T, U>
  > Exclude from T those types that are assignable to U
  
  > View on GitHub: https://tsch.js.org/43
*/


/* _____________ Your Code Here _____________ */

type MyExclude<T, U> = any


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, Exclude<'a' | 'b' | 'c', 'a'>>>,
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, Exclude<'a' | 'b' | 'c', 'a' | 'b'>>>,
  Expect<Equal<MyExclude<string | number | (() => void), Function>, Exclude<string | number | (() => void), Function>>>,
]



/* _____________ Further Steps _____________ */
/*
  > Share your solutions: https://tsch.js.org/43/answer
  > View solutions: https://tsch.js.org/43/solutions
  > More Challenges: https://tsch.js.org
*/
```

> ```typescript
> type MyExclude<T, U> = T extends U ? never : T;
> ```

此时`extends`用于==判定类型T是否可以分配给类型U==,而不能理解为类型T是类型U的一个子集

```typescript
//no error
type MyExclude<T, U> = T extends U ? never : T;

interface AA{
  prop1:number;
}

interface BB{
  prop1:number;
  prop2:number;
}

let temp:MyExclude<AA,BB>;
```

特别需要注意`never`类型(never是一切类型的子类型)

```typescript
type MyExclude<T, U> = T extends U ? never : T;

interface AA{
  prop1:number;
}

interface BB{
  prop1:number;
  prop2:number;
}

let temp:MyExclude<never,BB>;
```

## `keyof`关键字

`keyof`关键字通常用来描述对象类型,它会返回对应对象的数字或者字符串的`key`:

+ ```typescript
  type Point = { x: number; y: number };
  type P = keyof Point;
  //type p = "x" | "y"
  ```

+ ```typescript
  type Arrayish = { [n: number]: unknown };
  type A = keyof Arrayish;
  //type A=number;
  ```

+ ```typescript
  type Mapish = { [k: string]: boolean };
  type M = keyof Mapish;
  //type M= number | string
  ```

  

由于JS的原因,对象的key通常会被强制转换成`string`类型,因此上述M类型通常会被转换成number 或者 string类型



## `in`关键字

`in`关键字通常用来判断一个复合类型中是否存在左值表示的类型。类比JS中的`in`关键字(通常用来判断一个对象中是否存在某一个属性),TS中in关键字主要作用也是来缩小一个联合类型的范围的:

```typescript
type Fish = { swim: () => void };
type Bird = { fly: () => void };
 
function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
      //animal : fish
  }
 
  return animal.fly();
    //animal : bird
}
```

对于`in`关键字,需要特别注意可选类型,官网给了一个例子

> *To reiterate optional properties will exist in both sides for narrowing, for example a human could both swim and fly (with the right equipment) and thus should show up in both sides of the `in` check:*
>
> 大意大概是,一个人本身是不会像鱼一样游泳或者像鸟一样飞行的,但是携带正确的装备就可以办到,因此游泳和飞行对人类来说就是一个可选类型,因此,对于可选类型,会出现在检查的两侧=> `"swim" in animal`✅ `"swim" not in animal` ❎

```typescript
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
 
function move(animal: Fish | Bird | Human) {
  if ("swim" in animal) {
    animal;
      
//(parameter) animal: Fish | Human
  } else {
    animal;
      
//(parameter) animal: Bird | Human
  }
}
```

## `[]`语法

`[]`语法通常用来描述数组或者对象

例如:

```typescript
interface NumberArray {
    [index: number]: number;   //只要 index 的类型是 number,那么值的类型必须是 number。
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];//true

interface mp {
    [index: string]: number;   //只要 index 的类型是 string,那么值的类型必须是 number。
}
let fakemap: mp = {//true
    "haha":1,
    "heihei":2,
    "xixi":3
}
```