#typescript #typescript-typings #typescript2.0 #typescript-generics
#typescript #typescript-typings #typescript2.0 #typescript-generics
Вопрос:
Я хотел бы определить интерфейс в TypeScript следующим образом, но я понятия не имею, как это сделать:
export interface SomeInterface {
testProp:{
[key: string]: {
prop1: string;
prop2?: string;
prop3?: string;
....
};
}
requiredProps: ???? // <- here i would like to define type to be array of used keys
}
например, объект:
const value = {
testProp: {
orange: {
prop1: 'test1',
},
kiwi: {
prop1: 'random text',
},
lemon: {
prop1: 'text',
},
},
requiredProps: [] // posible items in array "lemon", "kiwi", "orange"
};
Я попытался определить requiredProps как requiredProps: [keyof Pick<SomeInterface,"testProp"]
, но безуспешно
Ответ №1:
Не существует определенного типа, соответствующего тому, что вы пытаетесь сделать. Однако вы можете представить его как универсальный тип. Например:
export interface SomeInterface<K extends PropertyKey, R extends K> {
testProp: Record<K, {
prop1: string;
prop2?: string;
prop3?: string;
}>;
requiredProps: R[]
}
Это будет ограничивать вещи так, как вы хотите, но для того, чтобы создать значение SomeInterface
типа, вам нужно указать параметры K
и R
. Вы можете заставить компилятор выводить их с помощью вспомогательной функции:
const asSomeInterface = <K extends PropertyKey, R extends K>(x: SomeInterface<K, R>) => x;
И используйте его так:
const value = asSomeInterface({
testProp: {
orange: {
prop1: 'test1',
},
kiwi: {
prop1: 'random text',
},
lemon: {
prop1: 'text',
},
},
requiredProps: ["orange", "kiwi"]
});
и вы можете увидеть желаемую ошибку при добавлении элемента requiredProps
, который не является ключом testProp
:
asSomeInterface({
testProp: {
a: { prop1: "" }, b: { prop1: "" }, c: { prop1: "" }
},
requiredProps: ["a", "b", "c", "d"] // error!
// --------------------------> ~~~~
// Type '"d"' is not assignable to type '"a" | "b" | "c"'
})
Создание SomeInterface<K, R>
универсального типа является более сложным в том смысле, что любые значения или функции, которые имеют с ними дело, должны содержать дополнительные параметры типа. Вы можете рассмотреть возможность использования общего кода только для кода, доступного внешним пользователям, ввод которых не гарантируется корректным, а затем внутренне расширить тип до неродовой версии, которая менее безопасна, но ее легче передавать:
// code seen by outside users, enforces constraint
function externalFunction<K extends string, R extends K>(
someInterface: SomeInterface<K, R>
) {
internalFunction(someInterface)
}
// code not exposed outside, widens to non-generic version
type SomeWiderInterface = SomeInterface<string, string>
const someWiderValue: SomeWiderInterface = value; // accepted
function internalFunction(someWiderInterface: SomeWiderInterface) {
// do stuff
}