中等

获取函数返回类型

不使用 ReturnType 实现 TypeScript 的 ReturnType<T> 泛型。 例如:

const fn = (v: boolean) => {
  if (v) return 1;
  else return 2;
};

type a = MyReturnType<typeof fn>; // 应推导出 "1 | 2"

解答

type MyReturnType<F> = F extends (...arg: any) => infer U ? U : never;

实现 Omit

不使用 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 MyExclude<T, P> = T extends P ? never : T;
type MyOmit<T, M> = {
  [p in MyExclude<keyof T, M>]: T[p];
};

对象部分属性可读

实现一个泛型 MyReadonly2<T, K>,它带有两种类型的参数 T 和 K。

类型 K 指定 T 中要被设置为只读 (readonly) 的属性。如果未提供 K,则应使所有属性都变为只读,就像普通的 Readonly<T>一样。

例如:

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

const todo: MyReadonly2<Todo, "title" | "description"> = {
  title: "Hey",
  description: "foobar",
  completed: false,
};

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

解答

type myInclude<T, C> = T extends C ? T : never;
type myExclude<T, C> = T extends C ? never : T;

type MyReadonly0<T, F> = {
  readonly [k in myInclude<keyof T, F>]: T[k];
};
type MyReadonly1<T, F> = {
  [k in myExclude<keyof T, F>]: T[k];
};

type MyReadonly2<T, F> = MyReadonly0<T, F> & MyReadonly1<T, F>;

对象属性只读(递归)

实现一个泛型 DeepReadonly<T>,它将对象的每个参数及其子对象递归地设为只读。

您可以假设在此挑战中我们仅处理对象。不考虑数组、函数、类等。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。

例如:

type X = {
  x: {
    a: 1;
    b: "hi";
  };
  y: "hey";
};

type Expected = {
  readonly x: {
    readonly a: 1;
    readonly b: "hi";
  };
  readonly y: "hey";
};

type Todo = DeepReadonly<X>; // should be same as `Expected`

解答

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends { [K: string]: {} } | any[]
    ? DeepReadonly<T[P]>
    : T[P];
};

元组转合集

实现泛型 TupleToUnion<T>,它返回元组所有值的合集。

例如:

type Arr = ["1", "2", "3"];

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

解答

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

可串联构造器

在 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;
  };
}

解答


例如:


解答


例如:


解答


例如:


解答


例如:


解答


例如:


解答


例如:


解答


例如:


解答


例如:


解答