#typescript #typescript-typings #typescript-generics
#typescript #typescript-типизации #typescript-generics
Вопрос:
Я пытаюсь ввести функцию, которая сопоставляет свойство в объекте с другим свойством.
Например mapProperty
, функция может использоваться как . mapProperty('_id', 'id', { _id: "arst" } )
Результирующий тип должен быть { id: string }
.
Я создал функцию mapProperty
, указанную ниже, но я чувствую as unknown as M
, что это не обязательно. Однако, когда я не использую это, я получаю следующую ошибку:
Тип
'T amp; { [x: string]: T[K]; }'
не может быть присвоен типу'M'
.
'M'
может быть создан экземпляр с произвольным типом, который может быть не связан с'T amp; { [x: string]: T[K]; }'
.
Также невозможно ввести возвращаемое значение как : { [key: L]: T[K] }
.
Каким может быть решение?
const mapProperty = <T, K extends keyof T, M, L extends keyof M>(
from: K,
to: L,
obj: T,
): M => {
const prop = obj[from];
delete obj[from];
return ({ ...obj, [to]: prop } as unknown) as M;
};
Редактировать: (Я использовал ответ Николай Гольцев с изюминкой)
Я добавил параметр, представляющий собой функцию, которая сопоставляет значение сопоставленного свойства:
const mapProperty = <T, K extends keyof T, L extends string, M = T[K]>(
obj: T,
from: K,
to: L,
fn?: (x: T[K]) => M,
): Omit<T, K> amp; { [key in L]: M } => {
const prop = fn ? fn(obj[from]) : obj[from];
delete obj[from];
return { ...obj, [to]: prop } as any;
};
Ответ №1:
Его можно ввести следующим образом:
const mapProperty = <T, K extends keyof T, L extends string>(
from: K,
to: L,
obj: T,
): Omit<T, K> amp; {
[key in L]: T[K]
} => {
const prop = obj[from];
delete obj[from];
return { ...obj, [to]: prop } as any;
};
const k = mapProperty('_id', 'id', { _id: "arst", k: true } )
Ошибка возникает в операторе return, потому delete
что оператор ничего не делает с типом переменной. После операции удаления это все равно будет T
. Вычисляемое свойство, как я знаю, всегда дает тип { [x: string]: T }
.
Ответ №2:
Пожалуйста, взгляните на следующее решение:
type Data = { age: 1, name: 'string' }
// Main utility
type Replace<O, P1 extends keyof O, P2 extends string> =
Pick<O, Exclude<keyof O, P1>>
amp; { [P in P2]: O[P1] }
type Result = Replace<Data, 'name', 'surname'>
const mapProperty = <O, P1 extends keyof O, P2 extends string>(obj: O, from: P1, to: P2): Replace<O, P1, P2> => {
const { [from]: replaced, ...rest } = obj;
// don't know how to get rid of casting operators
return {
...rest,
[to]: replaced
} as unknown as Replace<O, P1, P2>
}
const data: Data = { age: 1, name: 'string' }
const result = mapProperty(data, 'age', 'year')
type Res = keyof typeof result // "year" | "name"
Считается хорошей практикой вообще не использовать delete
operator , потому что он медленный и имеет другие недостатки. В качестве альтернативы вместо этого можно использовать Reflect.deleteProperty() . Я не знаю, как избавиться unknown
от приведения из-за вычисляемой природы [to] .