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
๋ ๋ ๋ฒ ์ ๋ฌ๋์ง ์์ต๋๋ค.
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๋ ์ด๋ค ํ์
์ด๋ ๋ ์ ์์ต๋๋ค. ์ ์ฝ์ด ์๊ธฐ ๋๋ฌธ์, ์ด ํ์
๋งค๊ฐ๋ณ์๋ ํจ์๊ฐ ํธ์ถ๋ ๋ ์ด๋ค ํ์
์ผ๋ก๋ ์ค์ ๋ ์ ์์ต๋๋ค.
์ ๋ค๋ฆญ ํ์
์ ์ฌ์ฉํ๋ ์ด์ ๋ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋ฉด ํจ์๋ ํด๋์ค๊ฐ ๋ค์ํ ํ์
์ ๋ํด ๋์ํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
/* _____________ ์ฌ๊ธฐ์ ์ฝ๋ ์
๋ ฅ _____________ */
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
๋ ๋ ๋ฒ ์ ๋ฌ๋์ง ์์ต๋๋ค.
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๋ ์ด๋ค ํ์
์ด๋ ๋ ์ ์์ต๋๋ค. ์ ์ฝ์ด ์๊ธฐ ๋๋ฌธ์, ์ด ํ์
๋งค๊ฐ๋ณ์๋ ํจ์๊ฐ ํธ์ถ๋ ๋ ์ด๋ค ํ์
์ผ๋ก๋ ์ค์ ๋ ์ ์์ต๋๋ค.
์ ๋ค๋ฆญ ํ์
์ ์ฌ์ฉํ๋ ์ด์ ๋ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋ฉด ํจ์๋ ํด๋์ค๊ฐ ๋ค์ํ ํ์
์ ๋ํด ๋์ํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
/* _____________ ์ฌ๊ธฐ์ ์ฝ๋ ์
๋ ฅ _____________ */
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;
}
่ฉฒๅฝใชใใธใงใฏใใใฎใใฎ
ใ้ขๆฐใฎ่ฟใๅคใซ่จญๅฎใใๅฟ
่ฆใใใใใใoption้ขๆฐ
ใฎ่ฟใๅคใฏChainable
ใจใชใใ
Chainable
ใฏๅๅผๆฐTใซใชใใธใงใฏใใๅใๅใ่ฟใๅคใง่ฟๅดใใใkey
ใซๆธกใใใๅๅผๆฐKey
ใ็พๅจใฎใชใใธใงใฏใใฎใญใผใซๅญๅจใใฆใใใฎใใๅคๅฎใChainable
ใใๆขๅญใฎใญใผใใญใใใฃใๅ้คใใใชใใธใงใฏใใจๆฐ่ฆใใชใฅใผใฎใใญใใใฃใใใผใธใใใ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
}
T = {}
gives a default value so type arguments is not requiredPropertyKey
in order to be passed to Record
, you can use string
here since there is only string in test casesK extends keyof T ? never : K
: K is never
type when key already exists in Chainable, any type except never
itself can pass to never
typeOmit
the old key every time we merge the new keyconst 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:
Record
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