如何在 Typescript 中将属性覆盖为不可为空

nic*_*ckf 11 node.js typescript typescript-typings

Node 内置 IncomingMessage((req, res, next)参数中的 req 类型)的绝对类型定义已定义url为 nullable。以下是定义文件的片段:

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

正如评论所说,这是因为此属性仅在您从 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'.
}
Run Code Online (Sandbox Code Playgroud)

值得一提的是,我将 TS 2.0.3 与 一起使用strictNullChecks,但在Typescript Playground上未启用。

这是问题。是否可以在我的应用程序中覆盖该定义,使其url不可为空?


这是我已经尝试过的……将其添加到我的一个文件中:

declare module 'http' {
  interface IncomingMessage {
    url: string;
  }
}
Run Code Online (Sandbox Code Playgroud)

...但是这是不允许的:“后续变量声明必须具有相同的类型”。这在文档中进行了解释。

到目前为止,我唯一能想到的就是创建我自己的模块,该模块导入、扩展然后导出接口:

// /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
}
Run Code Online (Sandbox Code Playgroud)

所以,这有效,但似乎错了。

Cyb*_*igy 22

在您的示例案例中,这很容易,因为您想摆脱 ALL undefined,因此使用Required实用程序类型。

interface IncomingMessage { url?: string; }
type ValidMessage = Required<IncomingMessage>;
Run Code Online (Sandbox Code Playgroud)

ValidMessage 将具有所有属性required

但对于那些来这里了解如何摆脱 ALL 的人null,您可以使用此自定义实用程序类型。

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

interface IncomingMessage { url: string | null; }
type ValidMessage = NonNullableFields<IncomingMessage>;
Run Code Online (Sandbox Code Playgroud)

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'>;
Run Code Online (Sandbox Code Playgroud)

ValidMessage 将具有属性url not null


nic*_*ckf 16

所以我找到了一个稍微不那么hacky的解决方案。

TypeScript 2.0 还添加了一个非空断言运算符!

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

就我而言,它仍然有点烦人,因为有许多不同的文件需要访问此属性,因此在许多地方都使用了此非空断言。


小智 10

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

IncomingMessage['url'] // string | undefined
Run Code Online (Sandbox Code Playgroud)

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

NonNullable<IncomingMessage['url']> // string
Run Code Online (Sandbox Code Playgroud)

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


nic*_*oga 9

这是定义实用程序类型的解决方案RequiredProperties

type RequiredProperties<T, P extends keyof T> = Omit<T, P> & Required<Pick<T, P>>;
Run Code Online (Sandbox Code Playgroud)

用法示例:

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
Run Code Online (Sandbox Code Playgroud)