Допустим, у меня есть интерфейс
interface ICart {
property1?: string,
propert2?: string,
someOtherProperty: string
}
Как я могу добиться, чтобы разрешено было только одно из property1 и property2, но одно из них должно быть там?
Допустим, у меня есть интерфейс
interface ICart {
property1?: string,
propert2?: string,
someOtherProperty: string
}
Как я могу добиться, чтобы разрешено было только одно из property1 и property2, но одно из них должно быть там?
Если вы хотите разрешить только одно свойство из списка, вам понадобится объединение типов объектов, каждый из которых разрешает одно свойство и запрещает все остальные. TypeScript не позволяет вам запретить конкретное свойство, но вы можете сделать что-то близкое: сделать его необязательным свойством с типом значения never
. На практике это позволит использовать свойство типа undefined
, но нет большой разницы между свойствами undefined
и отсутствующими свойствами (и разница в любом случае плохо фиксируется в TypeScript).
Итак, для вашего примера выше желаемый тип выглядит так:
type ICartManual = {
property1: string;
property2?: undefined;
someOtherProperty: string;
} | {
property1?: undefined;
property2: string;
someOtherProperty: string;
}
И вы можете убедиться, что он ведет себя так, как вам хочется:
const i1: ICartManual = {
property1: "prop1",
someOtherProperty: "other"
}
const i2: ICartManual = {
property2: "prop2",
someOtherProperty: "other"
}
const iBoth: ICartManual = { // error!
// ~~~~~ <-- property1 is incompatible with undefined
property1: "prop1",
property2: "prop2",
someOtherProperty: "other"
}
const iNeither: ICartManual = { // error!
// ~~~~~~~~ <-- property2 is missing
someOtherProperty: "other"
}
Если у вас большой интерфейс и вы хотите взять два типа объектов T
и U
и создать новый, для которого требуется ровно одно свойство из T
и все свойства из U
, вы можете определить его следующим образом:
type OneKeyFrom<T, M = {}, K extends keyof T = keyof T> = K extends any ?
(M & Pick<Required<T>, K> & Partial<Record<Exclude<keyof T, K>, never>>) extends infer O ?
{ [P in keyof O]: O[P] } : never : never;
Для этого используется набор mapped и условные типы для создания союза, который вы хотеть. Я мог бы объяснить, как это работает, но для этого потребуется много слов. Я делал подобное раньше; посмотрите здесь для более подробного описания подобного типа.
В любом случае, теперь мы можем определить ICart
вот так:
type ICart = OneKeyFrom<{ property1: string, property2: string }, { someOtherProperty: string }>;
и вы можете проверить (например, с помощью IntelliSense), что он совпадает с написанным вручную типом (за исключением порядка, в котором написаны свойства, который не меняет тип):
/* type ICart = {
property1: string;
property2?: undefined;
someOtherProperty: string;
} | {
property2: string;
property1?: undefined;
someOtherProperty: string;
} */
Хорошо, надеюсь, что это поможет; удачи!
// utility type which blocks two properties of the object coexisting
type NeverTogether<A extends object, Key1 extends keyof A, Key2 extends keyof A extends Key1 ? never : keyof A> =
Omit<A, Key1 | Key2> & (({
[k in Key1]: A[Key1]
} & {[k in Key2]?: never}) | ({
[k in Key1]?: never
} & {[k in Key2]: A[Key2]}))
interface ICart {
property1: string,
property2: string,
someOtherProperty: string
}
type IC = NeverTogether<ICart, 'property1', 'property2'>;
// error never together
const a: IC = {
property1: '1',
property2: '2',
someOtherProperty: '2'
}
// error one needs to be there
const b: IC = {
someOtherProperty: '2'
}
// correct
const c: IC = {
property2: '2',
someOtherProperty: '2'
}
// correct
const d: IC = {
property1: '1',
someOtherProperty: '2'
}
Проблема, связанная с типом NeverTogether
, заключается в его составлении, чтобы иметь такое правило для большего количества ключей. Так хорошо работает для двух зависимых полей, но не может работать больше. Но, возможно, это вам поможет. Для меня это была приятная головоломка.
{property1: string, property2?: never, someOtherProperty: string} | {property1?: never, property2: string, someOtherProperty: string}
должно работать - person jcalz   schedule 09.11.2019{ someOtherProperty: string }
на{someOtherProperty1:string, someOtherProperty2: string //and so on}
- person usr48   schedule 09.11.2019OneKeyFrom<T, U>
потребуются все свойства изU
и ровно одно свойство изT
. Поэтому, если вам нужно больше свойств, добавьте их вU
. Если у вас есть другие свойства для переключения, добавьте их вT
. Я напишу это как ответ, когда у меня будет время (если только кто-нибудь не доберется до него первым). Удачи! - person jcalz   schedule 09.11.2019