如何在Typescript中重写属性使其不可为空

bcs8qyzn  于 2023-02-10  发布在  TypeScript
关注(0)|答案(4)|浏览(200)

Node内置IncomingMessage的DefinitelyTyped定义((req, res, next)参数中req的类型)将url定义为可空。

// @types/node/index.d.ts
declare module "http" {
  export interface IncomingMessage {
    /**
     * Only valid for request obtained from http.Server.
     */
    url?: string;
  }
}

正如注解所述,这是因为只有当您从http.Server获取IncomingMessage的示例时,此属性才有效,而在其他情况下,它将不存在,因此可以为空。
然而,在我的例子中,* 我知道 * 我只能从http.server获得这些示例,所以如果没有额外的保护,我就不能访问属性,这有点烦人。

import { IncomingMessage, ServerResponse } from 'http';

function someMiddleware(req: IncomingMessage, res: ServerResponse, next: Function) {
  const myStr: string = req.url; // bzzzt.
  // Argument of type 'string | undefined' is not
  // assignable to parameter of type 'string'.
}
  • 最好说明一下,我正在strictNullChecks上使用TS 2.0.3,而Typescript Playground上没有启用该功能。*

问题是:是否可以在应用程序中覆盖该定义,使url不可以为空?
这是我已经尝试过的...添加到我的文件之一:

declare module 'http' {
  interface IncomingMessage {
    url: string;
  }
}

......但这是不允许的:“后续变量声明必须具有相同类型”. This is explained in the documentation.
到目前为止,我唯一能想到的就是创建自己的模块,用于导入、扩展和导出接口:

// /src/http.ts
import { IncomingMessage as OriginalIM } from 'http';
export interface IncomingMessage extends OriginalIM {
  url: string;
}

// src/myapp.ts
import { IncomingMessage } from './http'; // <-- local def

function someMiddleware(req: IncomingMessage) {
  const str: string = req.url; // all good
}

这是可行的,但似乎是错的

ojsjcaue

ojsjcaue1#

从TypeScript 2.1开始,可以使用查找类型访问接口属性。

IncomingMessage['url'] // string | undefined

您可以将其与NonNullable结合使用以满足您的用例。

NonNullable<IncomingMessage['url']> // string

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

3b6akqbq

3b6akqbq2#

所以我找到了一个解决方案,它稍微不那么古怪。
TypeScript 2.0还添加了一个非空Assert运算符:!

function someMiddleware(req: IncomingMessage) {
  const str1: string = req.url;  // error, can't assign string | undefined to string
  const str2: string = req.url!; // works
}

在我的例子中,这仍然有点烦人,因为有许多不同的文件需要访问这个属性,所以这个非空Assert在许多地方使用。

afdcj2ne

afdcj2ne3#

在您的示例中,这很容易,因为您希望删除所有undefined,因此使用Required实用程序类型。

interface IncomingMessage { url?: string; }
type ValidMessage = Required<IncomingMessage>;

ValidMessage将具有所有属性required
但是对于那些来这里了解如何摆脱ALL null的人来说,可以使用这个自定义实用程序类型。

export type NonNullableFields<T> = {
  [P in keyof T]: NonNullable<T[P]>;
};

interface IncomingMessage { url: string | null; }
type ValidMessage = NonNullableFields<IncomingMessage>;

ValidMessage将具有所有属性not null
对于那些来这里了解如何仅对特定字段摆脱null的人,可以使用这些定制的实用程序类型。

export type NonNullableFields<T> = {
  [P in keyof T]: NonNullable<T[P]>;
};

export type NonNullableField<T, K extends keyof T> = T &
NonNullableFields<Pick<T, K>>;

interface IncomingMessage { url: string | null; }
type ValidMessage = NonNullableField<IncomingMessage, 'url'>;

有效消息的属性为urlnot null

cgfeq70w

cgfeq70w4#

下面是定义实用程序类型RequiredProperties的解决方案:

type RequiredProperties<T, P extends keyof T> = Omit<T, P> & Required<Pick<T, P>>;

示例用法:

type Foo = {
    a?: any;
    b?: any;
    c?: any;
};

type Bar = RequiredProperties<Foo, 'a' | 'b'>;

const bar1: Bar = { a: 1, b: 2, c: 3 };
const bar2: Bar = { b: 2, c: 3 }; // fails because `a` is now required
const bar3: Bar = { c: 3 }; // fails because both `a` and `b` are missing

相关问题