00012-medium-chainable-options

Back

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

I couldn't find my solution under the accepted ones and many solutions with many upvotes don't allow overriding key-value pairs as required by the third test case. Many other solutions seem overly complicated.

type Chainable<Current = {}> = {
  option<K extends string, V>(key: K, value: V): Chainable<Omit<Current, K> & { [ Key in K as `${Key}` ]: V }>
  get(): Current
}

Type mapping allows for the creation of object types by mapping over union types. K can be thought of as a union with only one member.

The as keyword allows for the creation of a key name based on the value provided for key.

Before extending the current object Current, we must Omit from it any existing entry with the same key.

Solution by watisnu #34362

๋ฌธ์ œ ์„ค๋ช…

์ฒด์ธ ๊ฐ€๋Šฅ ์˜ต์…˜์€ ์ผ๋ฐ˜์ ์œผ๋กœ Javascript์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ TypeScript๋กœ ์ „ํ™˜ํ•˜๋ฉด ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

์ด ์ฑŒ๋ฆฐ์ง€์—์„œ๋Š” option(key, value)๊ณผ get() ๋‘๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฐ์ฒด(๋˜๋Š” ํด๋ž˜์Šค) ํƒ€์ž…์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ํƒ€์ž…์„ option์œผ๋กœ ์ง€์ •๋œ ํ‚ค์™€ ๊ฐ’์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๊ณ  get์œผ๋กœ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ

declare const config: Chainable;

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

// ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:
interface Result {
	foo: number;
	name: string;
	bar: {
		value: string;
	};
}

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด js/ts ๋กœ์ง์„ ์ž‘์„ฑํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋‹จ์ง€ ํƒ€์ž… ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค.

key๋Š” string๋งŒ ํ—ˆ์šฉํ•˜๊ณ  value๋Š” ๋ฌด์—‡์ด๋“  ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ key๋Š” ๋‘ ๋ฒˆ ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ’€์ด

options ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ์•ผ

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

<K extends string, V> ์™€ ๊ฐ™์€ ๋ถ€๋ถ„์€ ts์—์„œ ์ œ๋„ค๋ฆญ์„ ์„ ์–ธํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ฌธ์€ ํ•จ์ˆ˜์˜ ์ œ๋„ค๋ฆญ ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋‹ค์–‘ํ•œ ํƒ€์ž…์— ๋Œ€ํ•ด ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. V๋Š” ์–ด๋–ค ํƒ€์ž…์ด๋“  ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ์•ฝ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—, ์ด ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์–ด๋–ค ํƒ€์ž…์œผ๋กœ๋„ ์„ค์ •๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๊ฐ€ ๋‹ค์–‘ํ•œ ํƒ€์ž…์— ๋Œ€ํ•ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

solution

/* _____________ ์—ฌ๊ธฐ์— ์ฝ”๋“œ ์ž…๋ ฅ _____________ */

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

/* _____________ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค _____________ */
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")
	// @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;
};

Solution by adultlee #34139

๋ฌธ์ œ ์„ค๋ช…

์ฒด์ธ ๊ฐ€๋Šฅ ์˜ต์…˜์€ ์ผ๋ฐ˜์ ์œผ๋กœ Javascript์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ TypeScript๋กœ ์ „ํ™˜ํ•˜๋ฉด ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

์ด ์ฑŒ๋ฆฐ์ง€์—์„œ๋Š” option(key, value)๊ณผ get() ๋‘๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฐ์ฒด(๋˜๋Š” ํด๋ž˜์Šค) ํƒ€์ž…์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ํƒ€์ž…์„ option์œผ๋กœ ์ง€์ •๋œ ํ‚ค์™€ ๊ฐ’์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๊ณ  get์œผ๋กœ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ

declare const config: Chainable;

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

// ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:
interface Result {
	foo: number;
	name: string;
	bar: {
		value: string;
	};
}

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด js/ts ๋กœ์ง์„ ์ž‘์„ฑํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋‹จ์ง€ ํƒ€์ž… ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค.

key๋Š” string๋งŒ ํ—ˆ์šฉํ•˜๊ณ  value๋Š” ๋ฌด์—‡์ด๋“  ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ key๋Š” ๋‘ ๋ฒˆ ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ’€์ด

options ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ์•ผ

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

<K extends string, V> ์™€ ๊ฐ™์€ ๋ถ€๋ถ„์€ ts์—์„œ ์ œ๋„ค๋ฆญ์„ ์„ ์–ธํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ฌธ์€ ํ•จ์ˆ˜์˜ ์ œ๋„ค๋ฆญ ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋‹ค์–‘ํ•œ ํƒ€์ž…์— ๋Œ€ํ•ด ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. V๋Š” ์–ด๋–ค ํƒ€์ž…์ด๋“  ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ์•ฝ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—, ์ด ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์–ด๋–ค ํƒ€์ž…์œผ๋กœ๋„ ์„ค์ •๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๊ฐ€ ๋‹ค์–‘ํ•œ ํƒ€์ž…์— ๋Œ€ํ•ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

solution

/* _____________ ์—ฌ๊ธฐ์— ์ฝ”๋“œ ์ž…๋ ฅ _____________ */

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

/* _____________ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค _____________ */
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")
	// @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;
};

Solution by adultlee #34138

type Chainable = {
  option<K extends string, T>(key: K, value: T): SaveType<{
    [k in K]: T;
  }>;
}
type SaveType<Keys extends Record<string, any>> = {
  option<K extends string, T>(key: Exclude<K | keyof Keys, keyof Keys>, value: T): SaveType<{ [k in K]: T } & {
    [t in keyof Keys as t extends K ? never : t]: Keys[t];
  }>
  get(): {
    [t in keyof Keys]: Keys[t];
  }
}

Solution by LeonCry #34055

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 ouzexi #33988

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 notsecret32 #33890

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

Solution by laplace1009 #33717

// ไฝ ็š„็ญ”ๆกˆ
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 #33661

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

Solution by nupthale #33579

the answer is inspired from #598

type Chainable<U={}> = {
    option<TK extends string,TV>(k:Exclude<TK,keyof U>,v:TV):Chainable<U&{
        [P in Exclude<TK,keyof U>]: TV // double guarantee
    }>
    get():U
}

Solution by whatever8080 #33015

// ไฝ ็š„็ญ”ๆกˆ
type Chainable<T extends Record<string,any> = {}> = {
  option<K extends string,V extends any>(key: K extends keyof T?never: K, value: V): Chainable< {
    [key in keyof T as key extends K?never:key]:T[key]
  }& {
    [key in K]:V
  }>
  get(): T
}

Solution by walker-hzx #32934

// ่งฃ็ญ”ใ‚’ใ“ใ“ใซ่จ˜ๅ…ฅ
type Chainable<T extends object = {}> = {
  option: <Key extends string, Value>(
    key: Key extends keyof T
      ? never
      : Key,
    value: Value
  ) => Chainable<Omit<T, Key> & Record<Key, Value>>;
  get: () => T;
}

Solution by Yasunori-aloha #32813

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
}
  1. T = {} gives a default value so type arguments is not required
  2. K needs to be a PropertyKey in order to be passed to Record, you can use string here since there is only string in test cases
  3. K extends keyof T ? never : K: K is never type when key already exists in Chainable, any type except never itself can pass to never type
  4. From test case 2 & 3 we know that same key needs to be overwritten, so we Omit the old key every time we merge the new key
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 Expected2 = {
  name: string
}

type Expected3 = {
  name: number
}

I have a question though. Why overwrite duplicate key if you wants it to be an error?

Solution by MarvinXu #32713

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

type Chainable = {
    option: (arg1: string, arg2: number | string | { value: string }) => Chainable
    get: () => Result
}

declare const config: Chainable

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

Solution by ZhulinskiiDanil #32670

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

ๆฒกๅšๅ‡บๆฅ๏ผŒ่ฟ™้ข˜ๆœ‰็‚น้šพใ€‚ๆ€่€ƒๆ–นๅ‘ๅฎŒๅ…จ้”™ไบ†๏ผŒๆฒกๆœ‰ๆƒณๅˆฐ้€’ๅฝ’๏ผŒ่ฟ™ไธๅคชๅบ”่ฏฅใ€‚

NewK extends keyof T ? never : NewK ็”จๆฅ่ฟ‡ๆปคๅฝ“ NewK ๅทฒ็ปๅญ˜ๅœจ T ไธญ็š„ๆ—ถๅ€™๏ผŒ่ฝฌไธบ never๏ผŒไปŽ่€Œ่พพๅˆฐๆŠฅ้”™็š„็›ฎ็š„ใ€‚

ๅ…ถๅฎžๆˆ‘่ง‰ๅพ—ๅˆฐ่ฟ™้‡Œๅฐฑๅฏไปฅไบ†๏ผŒๅๆญฃๅทฒ็ปๆœ‰ๆŠฅ้”™ไบ†ใ€‚ไธๅคช้œ€่ฆๅŽ้ข็š„้‚ฃไธช Omit

Solution by mistkafka #32639

type Chainable<T = {}> = {
  option<R extends string, U>(key: R extends keyof T ? never : R, value: U): Chainable<{ [P in Exclude<keyof T, R>]: T[P] } & Record<R, U>>
  get(): T
}

Solution by nivenice #32367

// your answers

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

declare const config: Chainable

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



Solution by laqudee #32348

์ฒ˜์Œ ํ’€์ด: option ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ƒ๊ฐ์„ ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์ •์ ์ธ ํƒ€์ž…๋งŒ์„ ์ด์šฉํ•ด ์–ต์ง€๋กœ ํ’€์–ด๋ƒ„

type Chainable<Result extends Record<string, unknown> = {}> = {
  option<Key extends (Extract<keyof Result, Key> extends never ? string : never), Value>(key: Key, value: Value):
    Key extends never 
      ? never
      : Chainable<{ [key in keyof Result]: Result[key] } & Record<Key, Value>>
  get(): Result
}

๊ฐœ์„ ๋œ ํ’€์ด

type Chainable<Result = {}> = {
  option<Key extends string, Value>(key: Key extends keyof Result ? never : Key, value: Value):
    Chainable<Omit<Result, Key> & Record<Key, Value>>
  get(): Result
}

์•Œ์•„๊ฐˆ ๊ฒƒ: ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์— ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์„ ์ ์šฉ์‹œํ‚ฌ ์ƒ๊ฐ์„ ํ•˜์ž

Solution by dev-hobin #32220

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
}

We define Chainable type with a generic type T that extends from an object. The method get returns the generic T itself and the option method types as following:

Solution by joyanedel #32151

// your answers
type Chainable<T = {}> = {
  option<Key extends string, Value>(key: Key, value: Value): Chainable<Omit<T, Key> & Record<Key, Value>>
  get(): T
}

Solution by trinhvinhtruong96 #32054

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

Solution by pea-sys #31907