我需要一个 RxJS 运算符来检查对象上的属性是否存在,然后更改类型以正确反射(reflect)这一点。
我已经有一个运算符可以对发出的值执行此操作:
const notUndefined = <T>() =>
filter<T | undefined, T>(
(x): x is T extends undefined ? never : T => x !== undefined
);
但我还需要能够对其属性之一执行此操作,可以使用如下所示的属性:
interface MyInterface {
myKey?: string;
}
of<MyInterface>({ myKey: "Some value" }).pipe(
notUndefinedKey("myKey"),
map((x) => x.myKey)
// ^? (property) myKey: string
)
of<MyInterface>({ myKey: "Some value" }).pipe(
map((x) => x.myKey)
// ^? (property) MyInterface.myKey?: string | undefined
)
我已经有了第一个工作版本,但它似乎有点过分,而且似乎还从对象中删除了方法:
import { OperatorFunction, filter, of, map } from "rxjs";
// Typescript types helper
type DefinedFields<TType> = {
[TKey in keyof TType]-?: DefinedFields<NonNullable<TType[TKey]>>;
};
type WithDefinedKey<TType, TKey extends keyof TType> = Omit<TType, TKey> & DefinedFields<Pick<TType, TKey>>;
type OperatorToDefinedKey<TType, TKey extends keyof TType> = OperatorFunction<
TType | undefined,
WithDefinedKey<TType, TKey>
>;
// Operator
export const notUndefinedKey = <TType, TKey extends keyof TType>(
key: TKey
): OperatorToDefinedKey<TType, TKey> =>
filter<TType>((x) => x?.[key] !== undefined) as OperatorToDefinedKey<
TType,
TKey
>;
有什么好的方法可以做到这一点吗?一个可能还保留更多原始接口(interface)(例如对象上的方法)的对象?
最佳答案
您可以大大简化这个函数,因为我认为声明所有这些类型对您来说没有多大作用。下面的函数返回一个与传递的值类型相同的值,并且属性 key 的值已明确定义。
function notUndefinedKey<T, U extends keyof T>(key: U): OperatorFunction<T, T & { [K in U]-?: T[U] }> {
return filter((x): x is T & { [K in U]-?: T[U] } => x[key] != null);
}
用法示例
from([{}, { ignoreMe: 'Nope'}, {myKey: 'I am defined'}] as MyInterface[]).pipe(
notUndefinedKey ('myKey'),
).subscribe(x => console.log(x.myKey));
// output: I am defined
替代方案
如果您确实想要定义的类型(可能是因为语法看起来有点困惑),您可以执行以下操作:
type NotUndefinedKeys<T, U extends keyof T> = T & { [K in U]-?: T[U] }
function notUndefinedKey<T, U extends keyof T>(key: U): OperatorFunction<T, NotUndefinedKeys<T, U>> {
return filter((x): x is NotUndefinedKeys<T, U> => x[key] != null);
}
您还可以使用多个键来完成此操作,这可能是最灵活的用法,并且不需要太多更改。使用运算符根本不会改变 - 您可以传递单个 key 或多个 key 。运算符更改非常简单:接受类型的扩展运算符,更改过滤器函数的工作方式,然后 TypeScript 发挥其魔力。
function notUndefinedKeys<T, U extends keyof T>(...keys: U[]): OperatorFunction<T, T & { [K in U]-?: T[U] }> {
return filter((x): x is T & { [K in U]-?: T[U] } => keys.every(k => x[k] != null));
}
关于typescript - RxJS:确保定义了可选对象属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76565212/