如何让FlowType识别扩展后的部分选项对象在特定阶段变为完整对象
解决FlowType识别合并后完整选项对象的问题
嘿,这个场景太常见了!我来帮你搞定Flow类型识别的问题~首先先把你的代码贴出来方便分析:
type Options = { foo?: string }; class Something { static get defaultOptions(): Options { return { foo: "bar" }; } options: Options; constructor(options: Options) { this.options = Object.assign({}, options, Something.defaultOptions); } toString(): string { return `some thing: ${this.options.foo}`; } }
问题分析
Flow现在会认为this.options.foo可能是undefined,因为你定义的Options类型里foo是可选的。但实际上你在构造函数里已经和默认值合并了,foo肯定是存在的——Flow只是没法自动识别这个合并后的类型变化。另外这里还有个小逻辑bug:你当前的Object.assign顺序反了,应该把默认值放在前面,用户传入的选项放在后面,这样用户的选项才能覆盖默认值,不然默认值会把用户传入的foo给覆盖掉哦!
解决方案
方案1:显式定义完整类型 + 类型断言
最直接的方式是定义一个包含所有必填属性的完整类型,然后用类型断言告诉Flow合并后的结果是这个完整类型:
type Options = { foo?: string }; // 定义包含所有必填属性的完整类型 type FullOptions = { foo: string }; class Something { static get defaultOptions(): FullOptions { return { foo: "bar" }; } // 这里直接声明options为完整类型 options: FullOptions; constructor(options: Options) { // 修正Object.assign顺序,并用类型断言明确类型 this.options = (Object.assign({}, Something.defaultOptions, options): FullOptions); } toString(): string { return `some thing: ${this.options.foo}`; // Flow现在会识别foo肯定存在 } }
方案2:利用Flow交集类型
Flow的交集类型可以帮我们自动合并类型,让Flow推导出自定义选项和默认选项合并后的完整类型:
type Options = { foo?: string }; // 默认值的类型是包含必填foo的类型 type DefaultOptions = { foo: string }; class Something { static get defaultOptions(): DefaultOptions { return { foo: "bar" }; } // 用交集类型声明options的类型 options: DefaultOptions & Options; constructor(options: Options) { this.options = Object.assign({}, Something.defaultOptions, options); // Flow会自动推导交集类型中foo是必填的string } toString(): string { return `some thing: ${this.options.foo}`; } }
方案3:封装合并函数(更优雅)
把选项合并逻辑封装成一个单独的函数,给函数加上精确的类型注解,让Flow通过函数返回值自动识别完整类型:
type Options = { foo?: string }; type FullOptions = { foo: string }; // 专门的合并函数,明确输入输出类型 const mergeOptions = (userOptions: Options): FullOptions => { return Object.assign({}, { foo: "bar" }, userOptions); }; class Something { options: FullOptions; constructor(options: Options) { this.options = mergeOptions(options); } toString(): string { return `some thing: ${this.options.foo}`; } }
这几种方案都能解决你的问题,其中封装合并函数的方式更利于复用,也让类型逻辑更清晰~
内容的提问来源于stack exchange,提问作者Philipp Wrann
相关产品推荐
相关产品推荐

