Оригинальный PR для дискриминируемых объединений очень специфичен в отношении того факта, что различающее поле должно быть литеральным типом string
(с возможностью добавления поддержки для литеральных типов boolean
и number
).что, похоже, и произошло).Таким образом, ваш сценарий использования, в котором вы различаете данные по типу поля (string
против undefined
), не поддерживается.Это не работает, например:
let u!: { v: number, n: number } | { v: string, s: string}
if(typeof u.v === 'number') {
u.n // not accesible, type not narrowed
}
Мы могли бы использовать условные типы и охрану пользовательских типов, чтобы заставить вещи работать:
function isUndefined<T, K extends keyof T>(value : T, field: K) : value is Extract<T, { [P in K] : undefined }> {
return !!value[field]
}
export const getByField = (form: RepeatForm) => {
if (form.step.repeat === false || isUndefined(form.step, 'from')) {
return null;
}
const x = form.step.from;
return form.step.by;
};
Мы также можем создать общую версию этой функциичто позволяет сузить любой тип:
type ExtractKeysOfType<T, TValue> = { [P in keyof T]: T[P] extends TValue ? P : never}[keyof T]
function fieldOfType<T, K extends ExtractKeysOfType<T, string>>(value : T, field: K, type: 'string'): value is Extract<T, { [P in K] : string }>
function fieldOfType<T, K extends ExtractKeysOfType<T, number>>(value : T, field: K, type: 'number'): value is Extract<T, { [P in K] : number }>
function fieldOfType<T, K extends ExtractKeysOfType<T, boolean>>(value : T, field: K, type: 'boolean'): value is Extract<T, { [P in K] : boolean }>
function fieldOfType<T, K extends ExtractKeysOfType<T, Function>>(value : T, field: K, type: 'function'): value is Extract<T, { [P in K] : Function }>
function fieldOfType<T, K extends ExtractKeysOfType<T, symbol>>(value : T, field: K, type: 'symbol'): value is Extract<T, { [P in K] : symbol }>
function fieldOfType<T, K extends ExtractKeysOfType<T, object>>(value : T, field: K, type: 'object'): value is Extract<T, { [P in K] : object }>
function fieldOfType<T, K extends ExtractKeysOfType<T, undefined>>(value : T, field: K, type: 'undefined'): value is Extract<T, { [P in K] : undefined }>
function fieldOfType<T, K extends keyof T, TValue extends T[K]>(value : T, field: K, type: new (...args:any[])=> TValue): value is Extract<T, { [P in K] : TValue }>
function fieldOfType<T, K extends keyof T>(value : T, field: K, type: string| Function) :boolean {
if(typeof type === 'string') {
return typeof value[field] === type;
} else {
return value[field] instanceof type
}
}
const getByField = (form: RepeatForm) => {
if (form.step.repeat === false || fieldOfType(form.step, 'from', 'undefined')) {
return null;
}
const x = form.step.from;
return form.step.by;
};
let u: { v: number, n: number } | { v: string, s: string}={ v: 0, n : 10};
if(fieldOfType(u, 'v', 'number')) {
console.log(u.n);
}
class A {private a: undefined;}
class B {private b: undefined;}
let uc: { v: A, n: number } | { v: B, s: string} = Math.random() > 0.5 ? { v: new B(), s: '10' } : { v: new A(), n: 10 };
if(fieldOfType(uc, 'v', A)) {
console.log(uc.n)
}