Я хотел бы написать функцию, которая получает значение от объекта, заданного массивом ключей свойств. Это выглядело бы примерно так:
function getValue<O, K extends ObjKeys<O>>(obj: O, keys: K): ObjVal<O,K> {
let out = obj;
for (const k in keys) out = out[k]
return out
}
Я бы хотел, чтобы эта функция работала так:
type Foo = {
a: number
b: string
c: {
lark: boolean
wibble: string
}
}
let o: Foo = {
a: 1,
b: "hi",
c: {
lark: true,
wibble: "there"
}
}
// I'd like these to type check (and return the expected values):
getValue(o, ['a']) // returns 1
getValue(o, ['b']) // returns "hi"
getValue(o, ['c','lark']) // returns true
// I'd like these _not_ to type check:
getValue(o, ['a','b'])
getValue(o, ['d'])
Важно отметить, что я хотел бы иметь доступный тип (например, ObjKeys<O>
в приведенном выше примере), чтобы я мог легко использовать эту функцию из других функций, сохраняя при этом ввод. Например, я мог бы сделать что-то вроде этого:
function areValuesEqual<O>(obj: O, oldObj: O, keys: ObjKeys<O>) {
let value = getValue(obj, keys)
let oldValue = getValue(oldObj, keys)
return value === oldValue ? true : false
}
Эта функция принимает некоторые ключи и передает их в наш getValue
выше, и в идеале она должна проверять все типы, потому что объект O
и ключи ObjKeys<O>
являются действительными аргументами для getValue
функции, вызываемой внутри.
Это распространяется на возврат значения, возвращаемого getValue
; Я мог бы также сделать что-то вроде этого:
function doSomethingAndThenGetValue<O>(obj: O, oldObj: O, keys: ObjKeys<O>): ObjVal<O> {
let value = getValue(obj, keys)
console.log("Value obtained is:", value)
return value
}
Здесь также используется что-то вроде ObjVal<O>
, чтобы узнать, каким будет тип возвращаемого значения, и, следовательно, полная проверка типов.
Есть ли решение для этого, или просто нет возможности делать такие вещи в TypeScript в его нынешнем виде (версия 4 на момент написания)?
Лучшее, что у меня есть:
Я могу определить функцию, которая допускает вложенный доступ, примерно так:
function getValue<
O extends object,
K1 extends keyof O
>(obj: O, keys: [K1]): O[K1]
function getValue<
O extends object,
K1 extends keyof O,
K2 extends keyof O[K1]
>(obj: O, keys: [K1,K2]): O[K1][K2]
function getValue<
O extends object,
K1 extends keyof O,
K2 extends keyof O[K1],
K3 extends keyof O[K1][K2]
>(obj: O, keys: [K1,K2,K3]): O[K1][K2][K3]
function getValue<O>(obj: O, keys: Key | (Key[])): unknown {
let out = obj;
for (const k in keys) out = out[k]
return out
}
type Key = string | number | symbol
И затем этот тип проверяется правильно, когда я пытаюсь получить доступ к значениям (в данном случае до трех уровней).
Однако я немного застреваю, когда хочу использовать эту функцию от другого, сохраняя безопасность типов:
function areValuesEqual<O>(obj: O, oldObj: O, keys: ????) {
let value = getValue(obj, keys)
let oldValue = getValue(oldObj, keys)
return value === oldValue ? true : false
}
function doSomethingAndThenGetValue<O>(obj: O, oldObj: O, keys: ????): ???? {
let value = getValue(obj, keys)
console.log("Value obtained is:", value)
return value
}
Я не уверен, что я мог бы использовать вместо ????
, чтобы сообщить TypeScript, как типы соотносятся друг с другом, чтобы это могло вызвать проверку типов. Могу ли я избежать необходимости переписывать свой большой список перегрузок каждый раз, когда я хочу писать функции, подобные приведенным выше, но все же проверять типы, которые я хочу?