00012-medium-chainable-options

Back

type Chainable<T = {}> = {
  option<K extends PropertyKey, V>(key: K extends keyof T ? never : K, value: V): Chainable<Omit<T, K> & { [P in K]: V }>
  get(): {
    [P in keyof T]: T[P]
  }
}

Solution by LovePlayCode #37851

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

Solution by pkutsenko #37823

type Chainable<T = {}> = {
  option<K extends string, V>(
    key: K extends keyof T ? never : K, 
    value: K extends keyof T ? (V extends T[K] ? V : never) : V
  ): Chainable<Omit<T, K> & { [P in K]: V }>
  get(): T
}

Solution by AlexanderNP #37808

type Chainable<T = object> = { option: <K extends PropertyKey,V>(key: K,value: V) => Chainable<T & Record<K,V>>; get: () => T; }

Solution by jjojae9393 #37635

// your answers

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

Solution by PAVANT009 #37604

type Chainable<T = {}> = {
  option<K extends string, V>(key: K, value: V): Chainable<Omit<T, K> & Record<K, V>>;
  get(): T;
}
/* _____________ Test Cases _____________ */
import type { Alike, Expect } from '@type-challenges/utils'

declare const a: Chainable

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

const result2 = a
  .option('name', 'another name')
  .option('name', 'last name')
  .get()

const result3 = a
  .option('name', 'another name')
  .option('name', 123)
  .get()

type cases = [
  Expect<Alike<typeof result1, Expected1>>,
  Expect<Alike<typeof result2, Expected2>>,
  Expect<Alike<typeof result3, Expected3>>,
]

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

type Expected2 = {
  name: string
}

type Expected3 = {
  name: number
}

Solution by AnastasiaSv #37416

// your answers

Solution by AnastasiaSv #37415

逐步推演:

  1. 初始状态
type Chainable = {
  option(key: string, value: any): any;
  get(): any;
}
  1. 定义泛型返回类型 R, get() 返回 R
type Chainable<R = object> = {
  option(key: string, value: any): any;

  get(): R;
}
  1. option() 链式调用返回 Chainable
type Chainable<R = object> = {
  option(key: string, value: any): Chainable;

  get(): R;
}
  1. option<K, V>() 定义泛型 K, V;Chainable 泛型叠加类型声明
type Chainable<R = object> = {
  option<K extends string, V>(
    key: K, 
    value: V
  ): Chainable<R & Record<K, V>>;

  get(): R;
}
  1. Key 的类型不可重复
type Chainable<R = object> = {
  option<K extends string, V>(
    key: Exclude<K, keyof R>, 
    value: V
  ): Chainable<R & Record<K, V>>;

  get(): R;
}
  1. key类型不同时,允许覆盖 Omit<R, K>
type Chainable<R = object> = {
  option<K extends string, V>(
    key: Exclude<K, keyof R>, 
    value: V
  ): Chainable<Omit<R, K> & Record<K, V>>;

  get(): R;
}

Solution by djdidi #37146

// 여기 풀이를 입력하세요
type Chainable<Props = {}> = {
  option<K extends string, T>(key: K, value: T): Chainable<Omit<Props, K> & { [key in K]: T }>;
  get(): Props
}

Solution by seungdeok #36734

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

Solution by ChemieAi #36564

type Chainable<T = {}> = {
  option<K extends string, V>(key: K extends keyof T 
  ? never 
    : K, value: V)
    : Chainable<Omit<T, K> & Record<K, V>>
  get(): T
}

Solution by gakki-san #36438

type Chainable<T = {}> = {
  option<K extends keyof any,R>(
    key: K extends keyof T 
    ? R extends T[K]
    ? never : K 
    : K, value: R
    ): Chainable<Omit<T,K> & Record<K,R>>
  get(): T
}

Solution by gakki-san #36431

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

get(): T }

Solution by asylbekduldiev #36404

type Chainable<CurrentConfig = {}> = {
  option<OptionKey extends string, OptionValue>(
    key: Exclude<OptionKey, keyof CurrentConfig>,
    value: OptionValue
  ): Chainable<Omit<CurrentConfig, OptionKey> & Record<OptionKey, OptionValue>>;
  get(): CurrentConfig;
};

Solution by AleksandrShcherbackov #36161

type Chainable<T extends Record<keyof any, unknown> = {}> = {
  option<K extends keyof any, V>(
    key: K,
    value: V
  ): K extends keyof T
    ? Chainable<Omit<T, K> & Record<K, V>>
    : Chainable<Record<K, V> & T>
  get: () => T
}

Solution by Shunii85 #36041

type Chainable<R = {}> = {
  option<K extends string, V = any>(
    key: K extends keyof R ? never: K,
    value: V
  ): Chainable<Omit<R, K> & Record<K, V>>
  get(): R
}

Solution by user-9902 #36007

type Chainable<T = {}> = {
  option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => Chainable<Omit<T, K> & Record<K, V>>;
  get: () => T
}

explain:

declare const a: Chainable

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

we can find, a.option() can continue calling option or get, so we can kown option should return Chainable type, So

First, wo can add

type Chainable = {
  option: (key:string, value: any) => Chainable;
  get: any
}

Second, we need a generic parameter T to store type before the option call, and anthoer generic parameter K to avoid keys conflict, so we can get the code

type Chainable<T = {}> = {
  option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => Chainable<Omit<T, K> & Record<K, V>>;
  get: any
}

Thrid, we add get return type

type Chainable<T = {}> = {
  option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => Chainable<Omit<T, K> & Record<K, V>>;
  get: T
}

Solution by vaclock #35764

type Chainable<T = {}> = {
  option<K extends string, V>(
    key: K extends keyof T ? never : K,
    value: V
  ): Chainable<Omit<T, K> & Record<K, V>>;
  get(): T;
};

Solution by gangnamssal #35554

// 你的答案
// ============= Test Cases =============
import type { Alike, Expect } from "./test-utils";

declare const a: Chainable;

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

const result2 = a
  .option("name", "another name")
  // @ts-expect-error
  .option("name", "last name")
  .get();

const result3 = a
  .option("name", "another name")
  // @ts-expect-error
  .option("name", 123)
  .get();

type cases = [
  Expect<Alike<typeof result1, Expected1>>,
  Expect<Alike<typeof result2, Expected2>>,
  Expect<Alike<typeof result3, Expected3>>
];

type Expected1 = {
  foo: number;
  bar: {
    value: string;
  };
  name: string;
};

type Expected2 = {
  name: string;
};

type Expected3 = {
  name: number;
};

// ============= Your Code Here =============
type Chainable<T = {}> = {
  option<Key extends string, Value>(
    key: Key extends keyof T ? never : Key,
    value: Value
  ): Chainable<{
    [k in Key | keyof T]: k extends keyof T
      ? k extends Key
        ? Value
        : T[k]
      : Value;
  }>;
  get(): T;
};

Solution by run-nan #35476

type Chainable<T = {}> = {
  option: <K extends PropertyKey, V>(
    key: K extends keyof T ? never : K,
    value: V
  ) => Chainable<Omit<T, K> & Record<K, V>>;
  get: () => T;
};

Solution by RanungPark #35453

type Chainable<T = {}> = {
  option<K extends string|number, V extends unknown>(
      key: K extends keyof T
          ? V extends T[K]
              ? never
              : K
          : K,
      value: V
  ): Chainable<Omit<T, K> & { [P in K]: V }>;
  get(): T;
};

Solution by HrOkiG2 #35280

type Chainable<R = unknown> = {
  option<T extends string, V extends any>(key: T extends keyof R ? never : T, value: V): Chainable<Omit<R, T> & Record<T, V>>
  get(): R
}

Solution by eunsukimme #35121

type Chainable<T = {}> = {
  option<K extends string, V>(key: K, value: V): Chainable<Omit<T, K> & Record<K, V>>;
  get(): T;
};

Solution by 2njeong #35083

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

Solution by raeyoung-kim #35013

// 你的答案
type Chainable<T = {}> = {
  option<K extends string, V>(key: K extends keyof T ? never : K, value: V): Chainable<{
    [P in K]: V
  } & {
    [P in keyof T as P extends K ? never : P]: T[P]
  }>
  get(): T
}

Solution by heyuelan #34689

declare const config: Chainable<Result>

interface Result {
  foo: number
  name: string
  bar: {
    value: string
  }
}

type Chainable<T> = {
  options: <Key extends keyof T>(key: Key, val: T[Key]) => Chainable<Omit<T, Key>>
  get: () => T
}

const result = config.options('foo', 12)
  .options('name', 'foo')
  .options('bar', { value: 'bar' })
  .get()

Solution by semet #34645

type Chainable<T extends Record<string, any> = {}> = {
  option<K extends string, V extends any>(key: K extends keyof T ? never : K, value: V ): K extends keyof T ? Chainable<{ [P in K]: V }> : Chainable<T & { [P in K]: V }>
  get(): T
}
// your answers

Solution by Rustamaha #34597

type Chainable<T = {}> = {
  option: <K extends string, V>(key: K extends keyof T ? never
    : K, value: V) => Chainable<Omit<T, K> & Record<K, V>>
  get: () => T
}

Solution by devshinthant #34564

type Chainable<T = {}> = {
  option<K extends PropertyKey, V>(
    key: K extends keyof T ? never : K, value: V
  ): Chainable<Omit<T, K> & {[P in K]: V}>
  get(): {
    [P in keyof T]: T[P]
  } 
}

Solution by ktim816 #34434

type Chainable<T extends Record<string, any> = {}> = {
  option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => K extends keyof T ? Chainable<Omit<T, K> & Record<K, V>> : Chainable<T & Record<K, V>>
  get: () => T
}

Solution by rookie-luochao #34391