我有一个界面
interface IData {
importantData: string
}
,我想在那里添加isLoading
标志。如果为isLoading = false
,则将加载importantData
,并且肯定要在那里。但是,如果isLoading = true
,则importantData
可能存在,也可能不存在。
interface ILoadedData extends IData {
isLoading: false
}
interface ILoadingData extends Partial<IData> {
isLoading: true
}
所以,我的最终类型是这些的并集:
type IDataWithLoading = ILoadedData | ILoadingData
如果我尝试使用布尔文字,那么效果很好
const a:IDataWithLoading = ({ isLoading: false, importantData: 'secret' })
const b:IDataWithLoading = ({ isLoading: false }) // the only error, nice
const c:IDataWithLoading = ({ isLoading: true })
const d:IDataWithLoading = ({ isLoading: true, importantData: 'secret' })
但是,在编译时,我不知道是否正在加载东西,所以:
const random = Math.random() > 0.5
const e:IDataWithLoading = ({ isLoading: random, importantData: 'secret' }) // doesn't work
它抱怨类型不兼容。因为我声明了true
和false
的情况,而不是boolean
的情况,所以这很有道理。但是,我介绍了boolean
的所有可能情况,因此我感觉到TypeScript可以理解这一点,并且感觉到我做错了事。
但是,如果我明确声明boolean
,
interface ILoadingData extends Partial<IData> {
isLoading: boolean
}
它将isLoading = false
与Partial<IData>
匹配,这不是我想要的。我该怎么办?
答案 0 :(得分:2)
问题是您将Typescript用于错误的任务。 TS是关于类型而不是值的,因此它不知道
是否正在加载
所以您的界面应该看起来像这样
interface IData {
isLoading: boolean
importantData: string | null
}
更新
如果它看到isLoading = false并且没有,我试图让TS抱怨 重要数据,无论看到isLoading = true都不要抱怨 重要数据的存在。我100%确信有可能。
替换这段代码:
const random = Math.random() > 0.5
const e: IDataWithLoading = ({ isLoading: random, importantData: 'secret' })
对此:
const loaded = Math.random() > 0.5
let e:IDataWithLoading
if(loaded) {
e = { isLoading: false, importantData: 'secret' }
} else {
e = { isLoading: true, importantData: undefined }
}
如果我对您的理解正确,那应该是您所需要的全部。
答案 1 :(得分:2)
更新:2019年5月30日,TypeScript 3.5发行版应通过smarter union type checking解决。以下内容适用于3.4及以下版本:
这是已知的limitation of TypeScript。编译器不会尝试将联合传播到属性中。通常不会发生这种传播(例如,{a: string, b: string} | {a: number, b: number}
不能简化为{a: string | number, b: string | number}
或其他更有用的东西)。即使在像您这样可以做某事的情况下,编译器付出努力也不划算。通常,此类情况归结为手动指导编译器进行可能的状态。
例如,您可以尝试以下方法:
interface ILoadedDataButNotSureYet extends IData {
isLoading: boolean;
}
interface ILoadedData extends ILoadedDataButNotSureYet {
isLoading: false
}
因此,ILoadedDataButNotSureYet
确实加载了数据,但是isLoading
可能是true
或false
。然后,您可以将IDataWithLoading
表示为:
type IDataWithLoading = ILoadedDataButNotSureYet | ILoadingData;
这等效于您的原始定义,但是祝您好运,以使编译器注意到这一点。无论如何,您提到的所有特定实例仍然有效:
const a: IDataWithLoading = ({ isLoading: false, importantData: 'secret' })
const b: IDataWithLoading = ({ isLoading: false }) // the only error, nice
const c: IDataWithLoading = ({ isLoading: true })
const d: IDataWithLoading = ({ isLoading: true, importantData: 'secret' })
但最后一个也可以:
const random = Math.random() > 0.5
const e: IDataWithLoading = ({ isLoading: random, importantData: 'secret' }) // works
您可能想要或实际上不想更改IDataWithLoading
。另一种可行的方法是保留原始定义,并在对编译器的理解是安全的之前,对其进行足够的拍击。虽然不是很漂亮:
const eLit = { isLoading: random, importantData: 'secret' };
const e: IDataWithLoading = eLit.isLoading ?
{...eLit, isLoading: eLit.isLoading} : {...eLit, isLoading: eLit.isLoading};
// works, but is ugly
好在这里,我们使用object spread复制属性,并使用单独的isLoading
属性来利用检查eLit.isLoading
时发生的type guarding。三元运算符是多余的(“ then”和“ else”子句都是相同的),但是编译器会收到一条消息,指出每种情况下值均与IDataWithLoading
相匹配。就像我说的,祝你好运。
最后,您可以确定自己比编译器更聪明,并使用type assertion使其变得安静。这样做的好处是不会使您无所适从,而缺点是编译器无法在此处帮助您维护类型安全:
const e = {
isLoading: random,
importantData: 'secret'
} as IDataWithLoading; // Take that, compiler!
由您决定如何进行。希望能有所帮助;祝你好运!