我需要一个RxJS操作符,它可以判断对象上的属性是否存在,然后更改类型以正确反映这一点.

我已经有一个运算符,它将对发出的值执行此操作:

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
  >;

有什么好办法吗?它可能还保留了更多原始接口,如对象上的方法?

Playground Link

推荐答案

仅供参考,结合不同的 comments 和答案,我得出了以下结论:

type NeverUndefined<T> = T extends undefined ? never : T;

type WithNeverUndefined<T, K extends keyof T> = T & {
  [P in K]-?: NeverUndefined<T[P]>;
};

export const notUndefinedKey = <T, K extends keyof T>(key: K):
  OperatorFunction<T | undefined, WithNeverUndefined<T, K>> =>
  filter((x): x is WithNeverUndefined<T, K> => x?.[key] !== undefined);

// And the multi-key version from @daniel-gimenez
export const notUndefinedKeys = <T, K extends keyof T>(...keys: [K, ...K[]]): 
  OperatorFunction<T | undefined, WithNeverUndefined<T, K>> =>
  filter((x): x is WithNeverUndefined<T, K> => keys.every(key => x?.[key] !== undefined));

它确保对象和属性都不是未定义的. 它还适用于带有UNDEFINED的可选和类型联合:

interface MyInterface {
  myFirstKey?: string;
  mySecondKey: string | undefined;
  myThirdKey?: string | undefined;
}

of<MyInterface>(
  {
    myFirstKey: "Some value",
    mySecondKey: "Some value",
    myThirdKey: "Some value",
  }
).pipe(
  notUndefinedKeys("myFirstKey", "mySecondKey", "myThirdKey"),
  tap((x) => x.myFirstKey),
  //           ^? (property) MyInterface.myFirstKey: string
  tap((x) => x.mySecondKey),
  //           ^? (property) MyInterface.mySecondKey: string
  tap((x) => x.myThirdKey),
  //           ^? (property) MyInterface.myThirdKey: string
)

它还保留对象上的方法等.

最后,notUndefined的更新版:

export const notUndefined = <T>(): OperatorFunction<T | undefined, NeverUndefined<T>> =>
  filter((x): x is NeverUndefined<T> => x !== undefined);

Playground

Typescript相关问答推荐

contextPaneTitleText类型的参数不能分配给remoteconfig类型的关键参数'""'''

迁移到Reaction路由V6时,获取模块Reaction-Router-Dom';没有导出的成员RouteComponentProps错误

Typescript泛型类型:按其嵌套属性映射记录列表

来自类型脚本中可选对象的关联联合类型

向NextAuth会话添加除名称、图像和ID之外的更多详细信息

是否使用非显式名称隔离在对象属性上声明的接口的内部类型?

返回具有递归属性的泛型类型的泛型函数

为什么在回调函数的参数中不使用类型保护?

使用2个泛型类型参数透明地处理函数中的联合类型

使用KeyOf和Never的循环引用

刷新页面时,TypeScrip Redux丢失状态

(TypeScript)TypeError:无法读取未定义的属性(正在读取映射)"

有没有办法取消分发元组的联合?

专用路由组件不能从挂钩推断类型

如何避免从引用<;字符串&>到引用<;字符串|未定义&>;的不安全赋值?

如何编辑切片器以使用CRUD调用函数?

如何静态键入返回数组1到n的函数

如何使用angular15取消选中位于其他组件中的复选框

如何在Vuetify 3中导入TypeScript类型?

带有过滤键的 Typescript 映射类型