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
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// expect the type of result to be:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
type Chainable<T = {}> = {
option: <S extends string, V extends unknown>(s: S, v: V) => Chainable<T & Record<S, V>>
get(): T
}
Solution by anovicenko74 #31768
type Chainable<T = {}> = {
option<K extends string, V>(key: K, value: V): Chainable<Omit<T, K> & {[key in K]: V}>
get(): T
}
Solution by kai-phan #31620
// your answers
type Chainable<T extends object = {}> = {
option<K extends string, V>(
key: K,
value: V
): Chainable<{ [Key in keyof T]: T[Key] } & Record<K, V>>;
get(): {
[Key in keyof T]: T[Key];
};
};
Solution by AhmedRagabKamal #31487
type Chainable<T extends Record<string, any> = {}> = {
option<K extends string, V>(
key: K extends keyof T ? never : K,
value: V
): Chainable<Omit<T, K> & { [Key in K]: V }>
get(): T
}
Solution by LcsGa #31424
type Chainable<T = {}> = {
option: <Param extends string, Value>(
param: Param,
value: Value,
) => Chainable<
{
[key in keyof T as key extends Param ? never : key]: T[key];
} & {
[key in Param as key extends keyof T
? T[key] extends Value
? never
: key
: key]: Value;
}
>;
get: () => T;
};
Solution by arnokay #31398
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 jinyoung234 #31392
type Chainable<R = object> = {
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 vipulpathak113 #31258
// 你的答案
type Chainable<U = {}> = {
option<K extends string, T>(key: K extends keyof U ? never : K, value: T): Chainable<Omit<U, K> & Record<K, T>>
get(): U
}
Solution by TRIS-H #31226
type Chainable<Obj = {}> = {
option<k extends string,v extends any>(key: k extends keyof Obj ? never: k, value: v): Chainable<Omit<Obj, k> & Record<k, v>>
get(): Obj
Solution by eward957 #31208
SUPER EZ
// your answers
type Chainable<T extends object = {}> = {
option<KeyType extends string, ValueType>(key: Exclude<KeyType, keyof T>, value: ValueType): Chainable<Omit<T, KeyType> & Record<KeyType, ValueType>>
get(): T
}
Solution by kakasoo #31178
This is based on someone elses answer that didn't quite pass the tests and was over complicated.
type Chainable<R = object> = {
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 timrutter #31040
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
}
Solution by MyeonghoonNam #30846
type Chainable<T extends { [ a: string ]: any } = {}> = {
option<K extends string, V>(key: K extends keyof T ? never : K, value: V): Chainable<{ [ A in keyof T as A extends K ? never : A ]: T[ A ] } & { [ key in K ]: V }>,
get(): { [ A in keyof T as A ]: T[ A ] }
}
Solution by vprokashev #30791
type Chainable<T = {}> = {
option<K extends string, V>(key: Exclude<K, keyof T>, value: V): Chainable<Omit<T,K>& Record<K,V>>
get(): T
}
Solution by her0707 #30570
type Chainable<T extends { [key: string]: any } = { [key: string]: any }> = {
option<K extends string, V>(key: K, value: Readonly<V>): Chainable< T & Record<K, Readonly<V>> >
get(): T
}
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// expect the type of result to be:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
Solution by rxMATTEO #30078
type Chainable<T extends {}> = {
option<K extends string, V>(key: K, value: V): T & {[k in K]: V}
get(): { [P in keyof T]: T[P]}
}
Solution by somasekimoto #29925
type Chainable<Result = {}> = {
option<TKey extends string, TValue>(key: TKey, value: TValue): Chainable<Omit<Result, TKey> & { [key in TKey]: TValue }>
get(): Result
}
Solution by gstcarv #29911