如何解决为联合类型和泛型编写巧妙的类型保护
我有以下结构:
const fragmentTypes = [
'Word','Sentence',] as const;
type FragmentType = typeof fragmentTypes[number];
interface IFragmentData {
type: FragmentType;
}
interface IFragment<T extends IFragmentData> {
id: string;
type: T['type'];
data: Omit<T,'type'>;
}
interface IWordFragmentData extends IFragmentData {
type: 'Word';
word: string;
}
interface ISentenceFragmentData extends IFragmentData {
type: 'Sentence';
sentence: string;
}
type Fragment =
| IFragment<IWordFragmentData>
| IFragment<ISentenceFragmentData>;
并且知道我经常filter
碎片的挑战。我目前的方法是通过以下类型保护:
function isFragmentType<T extends IFragmentData>(t: FragmentType) {
return (x: Fragment | IFragment<T>): x is IFragment<T> => {
return x.type === t;
};
}
console.log(isFragmentType<IWordFragmentData>('Word')({type: 'Word',id: 'test123',data: {word: 'test123'}}));
这工作正常,但留下了将 IFragmentData
与错误的 FragmentType
组合的选项。例如: isFragmentType<IMarkFragmentData>('Sentence')
将是有效代码,即使 'Sentence' 是 IMarkFragmentData
类型的错误鉴别器。
有没有更聪明的方法来编写我的类型保护甚至重构我的输入?
解决方法
isFragmentType()
函数的主要问题是 t
的类型根本不限于 T
。我可能会重写它,以便 T
表示 type
属性,并使用 the Extract
utility type 过滤具有该 Fragment
属性的成员的 type
联合:
function isFragmentType<T extends Fragment['type']>(t: T) {
return (x: Fragment): x is Extract<Fragment,{ type: T }> => {
return x.type === t;
};
}
您可以验证这是否按预期工作(并且您不必手动指定 T
,因为它可以从 t
的类型推断):
function processFragment(f: Fragment) {
if (isFragmentType("Word")(f)) {
f.data.word.toUpperCase(); // okay
} else {
f.data.sentence.toUpperCase(); // okay
}
}
仅供参考,我不确定为什么 isFragmentType()
是 curried,但它看起来并不需要:
function isFragmentType<T extends Fragment['type']>(
t: T,x: Fragment
): x is Extract<Fragment,{ type: T }> {
return x.type === t;
}
function processFragment(f: Fragment) {
if (isFragmentType("Word",f)) {
f.data.word.toUpperCase(); // okay
} else {
f.data.sentence.toUpperCase(); // okay
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。