ForeveHG / Frontend-Daily-Interview

学习,尝试回答一些前端面试题
1 stars 0 forks source link

101. TS 练习 #102

Open ForeveHG opened 2 years ago

ForeveHG commented 2 years ago

题目

实现 TS 内置的 Pick<T, K>,但不可以使用它。

从类型 T 中选择出属性 K,构造成一个新的类型。

例如:

  interface Todo {
    title: string
    description: string
    completed: boolean
  }

  type TodoPreview = MyPick<Todo, 'title' | 'completed'>

  const todo: TodoPreview = {
      title: 'Clean room',
      completed: false,
  }

实现:

type MyPick<T, K extends keyof T> = {
  [key in K]: T[key]
}

/* _____________ 测试用例 _____________ */
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Expected1, MyPick<Todo, 'title'>>>,
  Expect<Equal<Expected2, MyPick<Todo, 'title' | 'completed'>>>,
  // @ts-expect-error
  MyPick<Todo, 'title' | 'completed' | 'invalid'>,
]

interface Todo {
  title: string
  description: string
  completed: boolean
}

interface Expected1 {
  title: string
}

interface Expected2 {
  title: string
  completed: boolean
}
ForeveHG commented 2 years ago

题目

不要使用内置的Readonly<T>,自己实现一个。

Readonly 会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会被 readonly 所修饰。

也就是不可以再对该对象的属性赋值。

例如:

  interface Todo {
    title: string
    description: string
  }

  const todo: MyReadonly<Todo> = {
    title: "Hey",
    description: "foobar"
  }

  todo.title = "Hello" // Error: cannot reassign a readonly property
  todo.description = "barFoo" // Error: cannot reassign a readonly property

实现

type MyReadonly<T> = {
  readonly [key in keyof T]: T[key]
}

/* _____________ 测试用例 _____________ */
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyReadonly<Todo1>, Readonly<Todo1>>>,
]

interface Todo1 {
  title: string
  description: string
  completed: boolean
  meta: {
    author: string
  }
}
ForeveHG commented 2 years ago

题目 元组转换为对象

传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。

例如:

  const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

  type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

实现

type TupleToObject<T extends readonly string[]> = {
  [key in T[number]]: key
}

思路

in 操作符: 遍历联合类型 T[number]: 元祖转联合类型

ForeveHG commented 2 years ago

题目 元组转合集

实现泛型TupleToUnion<T>,它覆盖元组的值与其值联合。

例如

  type Arr = ['1', '2', '3']

  type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'

实现

type TupleToUnion<T extends any[]> = T[number]

实现2

type TupleToUnion<T> = T extends (infer U)[] ? U : never

//或者
type TupleToUnion<T> = T extends Array<infer U> ? U : never
ForeveHG commented 2 years ago

元祖转嵌套对象

给一个只包含string类型的元祖T和一个类型U,实现一个嵌套对象

  type a = TupleToNestedObject<['a'], string> // {a: string}
  type b = TupleToNestedObject<['a', 'b'], number> // {a: {b: number}}
  type c = TupleToNestedObject<[], boolean> // boolean. if the tuple is empty, just return the U type

实现

type TupleToNestedObject<T,U> = T extends [infer First, ...infer Rest] ? First extends string ? {
  [key in First]: TupleToNestedObject<Rest, U>
} : never : U

/ 测试用例 / import { Equal, Expect, ExpectFalse, NotEqual } from '@type-challenges/utils'

type cases = [ Expect<Equal<TupleToNestedObject<['a'], string>, {a: string}>>, Expect<Equal<TupleToNestedObject<['a', 'b'], number>, {a: {b: number}}>>, Expect<Equal<TupleToNestedObject<['a', 'b', 'c'], boolean>, {a: {b: {c: boolean}}}>>, Expect<Equal<TupleToNestedObject<[], boolean>, boolean>>, ]

/ Further Steps / /*

Share your solutions: https://tsch.js.org/3188/answer View solutions: https://tsch.js.org/3188/solutions More Challenges: https://tsch.js.org */

ForeveHG commented 2 years ago

题目 实现内置的Exclude <T,U>类型,但不能直接使用它本身。

从联合类型T中排除U的类型成员,来构造一个新的类型。

实现

type Exclude<T,U> = T extends U ? never : T ;

/ 测试用例 / import { 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>>>, ]

ForeveHG commented 2 years ago

题目 获取元祖长度

创建一个通用的Length,接受一个readonly的数组,返回这个数组的长度。

例如:

  type tesla = ['tesla', 'model 3', 'model X', 'model Y']
  type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

  type teslaLength = Length<tesla> // expected 4
  type spaceXLength = Length<spaceX> // expected 5

实现

type Length<T extends readonly any[]> = T['length']

/ 测试用例 / import { Equal, Expect } from '@type-challenges/utils'

const tesla = ['tesla', 'model 3', 'model X', 'model Y'] as const const spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] as const

type cases = [ Expect<Equal<Length, 4>>, Expect<Equal<Length, 5>>, // @ts-expect-error Length<5>, // @ts-expect-error Length<'hello world'>, ]

ForeveHG commented 2 years ago

题目 实现一个 IF 类型

实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 FC 只能是 true 或者 falseTF 可以是任意类型。

举例:

  type A = If<true, 'a', 'b'>  // expected to be 'a'
  type B = If<false, 'a', 'b'> // expected to be 'b'

实现

type If<C extends boolean, T, F> = C extends true ? T : F;

/ 测试用例 / import { Equal, Expect } from '@type-challenges/utils'

type cases = [ Expect<Equal<If<true, 'a', 'b'>, 'a'>>, Expect<Equal<If<false, 'a', 2>, 2>>, ]

// @ts-expect-error type error = If<null, 'a', 'b'>

ForeveHG commented 2 years ago

题目 concat

在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。

举例:

  type Result = Concat<[1], [2]> // expected to be [1, 2]

实现

type Concat<T extends any[], U extends any[]> = [...T, ...U]

测试用例

import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Concat<[], []>, []>>,
  Expect<Equal<Concat<[], [1]>, [1]>>,
  Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>,
  Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>,
]
ForeveHG commented 2 years ago

题目 Includes

在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false

举例:

  type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`

实现

type Includes<T extends readonly any[], U> =  T extends [infer First, ...infer Rest] ? [First, U] extends [U, First] ? true : Includes<Rest, U> : false

该实现不能满足测试用例,因为extends会忽略readonly等修饰符:

  Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
  Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,

另外一种实现方式:

type Includes<T extends readonly any[], U> =  T extends [infer First, ...infer Rest] ? IsEqual<First, U> extends true ? true : Includes<Rest, U> : false

可以满足所有测试用例,但目前不太明白原理,参考 https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796,暂时理解为: ts条件类型(extends)中泛型的可分配性的规则要求,extends后的类型要与类型检查器(isTypeIdenticalTo)的定义相同

declare let x: <T>() => (T extends {a: string} ? 1 : 2)
declare let y: <T>() => (T extends {readonly a: string} ? 1 : 2)
y = x // error

测试用例

/ 测试用例 / import { Equal, Expect } from '@type-challenges/utils'

type cases = [ Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>, Expect<Equal<Includes<['Kars', 'Esidisi','Wamuu', 'Santana'], 'Dio'>, false>>, Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>, Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>, Expect<Equal<Includes<[1, 2, 3], 2>, true>>, Expect<Equal<Includes<[1, 2, 3], 1>, true>>, Expect<Equal<Includes<[{}], { a: 'A' }>, false>>, Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>, Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>, Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>, Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>, Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>, ]

ForeveHG commented 2 years ago

题目 Push

在类型系统里实现通用的 Array.push

举例如下,

  type Result = Push<[1, 2], '3'> // [1, 2, '3']

实现

type Push<T extends any[], U> = [...T, U];

测试

/* _____________ 测试用例 _____________ */
import { Equal, Expect, ExpectFalse, NotEqual } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Push<[], 1>, [1]>>,
  Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
  Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>,
]
ForeveHG commented 2 years ago

题目 Unshift

实现类型版本的 Array.unshift

举例,

  type Result = Unshift<[1, 2], 0> // [0, 1, 2,]

实现

type Push<T extends any[], U> = [U, ...T];

测试

/* _____________ 测试用例 _____________ */
import { Equal, Expect, ExpectFalse, NotEqual } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Unshift<[], 1>, [1]>>,
  Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2,]>>,
  Expect<Equal<Unshift<['1', 2, '3'],boolean>, [boolean, '1', 2, '3']>>,
]
ForeveHG commented 2 years ago

题目 Parameters

实现内置的 Parameters 类型,而不是直接使用它,可参考TypeScript官方文档

实现

type MyParameters<T extends (...args: any[]) => any> = T extends (...x: infer U) => any ? U : never

测试用例

/* _____________ 测试用例 _____________ */
import { Equal, Expect, ExpectFalse, NotEqual } from '@type-challenges/utils'

const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: {a: 'A'}): void => {}
const baz = (): void => {}

type cases = [
  Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
  Expect<Equal<MyParameters<typeof bar>, [boolean, {a: 'A'}]>>,
  Expect<Equal<MyParameters<typeof baz>, []>>,
]

/* _____________ 下一步 _____________ */
/*
  > 分享你的解答:https://tsch.js.org/3312/answer/zh-CN
  > 查看解答:https://tsch.js.org/3312/solutions
  > 更多题目:https://tsch.js.org/zh-CN
*/
ForeveHG commented 2 years ago

题目 Awaited

假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

比如:Promise<ExampleType>,请你返回 ExampleType 类型。

这个挑战来自于 @maciejsikora 的文章:original article

在 Github 上查看:https://tsch.js.org/189/zh-CN

实现

// type MyAwaited<T> = T extends Promise<infer U> ? U extends Promise<infer Z> ? MyAwaited<Promise<Z>> : U : never
type MyAwaited<T> = T extends Promise<infer I>? MyAwaited<I> : T

测试用例

/* _____________ 测试用例 _____________ */
import { Equal, Expect } from '@type-challenges/utils'

type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>

type cases = [
  Expect<Equal<MyAwaited<X>, string>>,
  Expect<Equal<MyAwaited<Y>, { field: number }>>,
  Expect<Equal<MyAwaited<Z>, string | number>>,
]

// @ts-expect-error
type error = MyAwaited<number>
ForeveHG commented 2 years ago

题目

不使用 Omit 实现 TypeScript 的 Omit<T, K> 范型。

Omit 会创建一个省略 K 中字段的 T 对象。

例如:

  interface Todo {
    title: string
    description: string
    completed: boolean
  }

  type TodoPreview = MyOmit<Todo, 'description' | 'title'>

  const todo: TodoPreview = {
    completed: false,
  }

实现

type MyOmit<T, K> = {
  [key in Exclude<keyof T,K>]: T[key]
}
ForeveHG commented 2 years ago

题目 Tuple to Union

Implement a generic TupleToUnion<T> which covers the values of a tuple to its values union.

  type Arr = ['1', '2', '3']

  type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'

实现

type TupleToUnion<T extends any[]> = T[number]

测试用例

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

type cases = [
  Expect<Equal<TupleToUnion<[123, '456', true]>, 123 | '456' | true>>,
  Expect<Equal<TupleToUnion<[123]>, 123>>,
]
ForeveHG commented 2 years ago

题目 最后一个元素

实现一个通用Last<T>,它接受一个数组T并返回其最后一个元素的类型。

例如

  type arr1 = ['a', 'b', 'c']
  type arr2 = [3, 2, 1]

  type tail1 = Last<arr1> // expected to be 'c'
  type tail2 = Last<arr2> // expected to be 1

实现

type Last<T extends any[]> = T extends [...infer Rest, infer Last] ? Last : never

测试用例

/* _____________ 测试用例 _____________ */
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Last<[3, 2, 1]>, 1>>,
  Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>,
]

/* _____________ 下一步 _____________ */
/*
  > 分享你的解答:https://tsch.js.org/15/answer/zh-CN
  > 查看解答:https://tsch.js.org/15/solutions
  > 更多题目:https://tsch.js.org/zh-CN
*/
ForeveHG commented 2 years ago

题目 可串联构造器

在 JavaScript 中我们很常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给他附上类型吗?

在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value)get()。在 option 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get 获取最终结果。

例如

  declare const config: Chainable

  const result = config
    .option('foo', 123)
    .option('name', 'type-challenges')
    .option('bar', { value: 'Hello World' })
    .get()

  // 期望 result 的类型是:
  interface Result {
    foo: number
    name: string
    bar: {
      value: string
    }
  }

你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。

你可以假设 key 只接受字符串而 value 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 key 只会被使用一次。

实现

// type Chainable<T = {}> = {
//   option<K extends string, U>(key: K, value: U): Chainable<{[key in K]: U} & T>
//   get(): T
// }

// abstract class Chainable<T = {}> {
//   abstract option<K extends string, U>(key: K, value:U):Chainable<{[key in K]: U} & T> 
//   abstract get():T
// }

interface Chainable<T = {}> {
  option<K extends string, U>(key: K, value: U): Chainable<{[key in K]: U} & T>
  get(): T
}

功能实现:

interface Chainable<T = {}> {
  option<K extends string, U>(key: K, value: U): Chainable<{[key in K]?: U} & T>
  get(): T
}

let obj:{[key:string]: any} = {}
let C: Chainable = {
  option(key:string, value:any) {
    obj[key] = value
    return C
  },
  get() {
    return obj
  }
}

let c = C.option('age', 12).option('name', 'jiali').get()

测试用例

/* _____________ 测试用例 _____________ */
import { Alike, Expect } from '@type-challenges/utils'

declare const a: Chainable

const result = a
  .option('foo', 123)
  .option('bar', { value: 'Hello World' })
  .option('name', 'type-challenges')
  .get()

typeof result

type cases = [
  Expect<Alike<typeof result, Expected>>
]

type Expected = {
  foo: number
  bar: {
    value: string
  }
  name: string
}

/* _____________ 下一步 _____________ */
/*
  > 分享你的解答:https://tsch.js.org/12/answer/zh-CN
  > 查看解答:https://tsch.js.org/12/solutions
  > 更多题目:https://tsch.js.org/zh-CN
*/
ForeveHG commented 2 years ago

题目 Type Lookup

有时,您可能希望根据其属性在并集中查找类型。

在此挑战中,我们想通过在联合Cat | Dog中搜索公共type字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, 'dog'>获得DogLookUp<Dog | Cat, 'cat'>获得Cat

  interface Cat {
    type: 'cat'
    breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
  }

  interface Dog {
    type: 'dog'
    breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
    color: 'brown' | 'white' | 'black'
  }

  type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`

实现

type LookUp<U, T> = U extends {type: T} ? U : never;

测试用例

/* _____________ 测试用例 _____________ */
import { Equal, Expect } from '@type-challenges/utils'

interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type Animal = Cat | Dog

type cases = [
  Expect<Equal<LookUp<Animal, 'dog'>, Dog>>,
  Expect<Equal<LookUp<Animal, 'cat'>, Cat>>,
]

/* _____________ 下一步 _____________ */
/*
  > 分享你的解答:https://tsch.js.org/62/answer/zh-CN
  > 查看解答:https://tsch.js.org/62/solutions
  > 更多题目:https://tsch.js.org/zh-CN
*/