TS2339:联合类型上不存在 属性 - 属性 字符串 |不明确的
TS2339: Property does not exist on union type - property string | undefined
我的联合类型有问题,如下所示:
type RepeatForm = {
step:
| {
repeat: false;
}
| {
repeat: true;
from: undefined;
}
| {
repeat: true;
from: string;
by?: string;
};
};
我有以下函数,我想在其中获取 by
的值(如果它存在):
export const getByField = (form: RepeatForm) => {
if (form.step.repeat === false || form.step.from === undefined) {
return null;
}
const x = form.step.from;
return form.step.by;
};
我收到此错误:Property 'by' does not exist on type '{ repeat: true; from: undefined; } | { repeat: true; from: string; by?: string | undefined; }'.
Property 'by' does not exist on type '{ repeat: true; from: undefined; }'.
这让我非常困惑,因为 TypeScript 知道 form.step.from
与 undefined
不同,他甚至将变量类型 x
插入到 string
。
这个问题的原因是什么?那我怎样才能访问by
属性呢?
用于区分联合的原始 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)
}
我的联合类型有问题,如下所示:
type RepeatForm = {
step:
| {
repeat: false;
}
| {
repeat: true;
from: undefined;
}
| {
repeat: true;
from: string;
by?: string;
};
};
我有以下函数,我想在其中获取 by
的值(如果它存在):
export const getByField = (form: RepeatForm) => {
if (form.step.repeat === false || form.step.from === undefined) {
return null;
}
const x = form.step.from;
return form.step.by;
};
我收到此错误:Property 'by' does not exist on type '{ repeat: true; from: undefined; } | { repeat: true; from: string; by?: string | undefined; }'.
Property 'by' does not exist on type '{ repeat: true; from: undefined; }'.
这让我非常困惑,因为 TypeScript 知道 form.step.from
与 undefined
不同,他甚至将变量类型 x
插入到 string
。
这个问题的原因是什么?那我怎样才能访问by
属性呢?
用于区分联合的原始 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)
}