Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override a property to be non-nullable in Typescript

The DefinitelyTyped definition of the Node built-in IncomingMessage (the type of req in the (req, res, next) arguments) has defined url to be nullable. Here's the snipped parts of the definition file:

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

As the comment says, this is because this property is only valid when you're getting an instance of this IncomingMessage from the http.Server. In other uses it won't exist, hence, it's nullable.

However, in my case, I know that I'm only getting these instances from http.Server, and so it's kinda annoying that I can't just access the property without extra guards.

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'.
}

It's probably good to mention that I'm using TS 2.0.3 with strictNullChecks, which is not enabled on the Typescript Playground.

Here's the question. Is it possible to override that definition across my application so that url is not nullable?


Here's what I've already tried... adding this to one of my files:

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

...however that is disallowed: "Subsequent variable declarations must have the same type". This is explained in the documentation.

The only thing I can think of thus far is to create my own module which imports, extends and then exports the interfaces:

// /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
}

So, this works, but it seems so wrong.

like image 695
nickf Avatar asked Nov 01 '16 23:11

nickf


People also ask

How do I override a properties in TypeScript?

Use the Omit utility type to override the type of an interface property, e.g. interface SpecificLocation extends Omit<Location, 'address'> {address: newType} . The Omit utility type constructs a new type by removing the specified keys from the existing type.

How do I make my properties optional in TypeScript?

Use the Partial utility type to make all of the properties in a type optional, e.g. const emp: Partial<Employee> = {}; . The Partial utility type constructs a new type with all properties of the provided type set to optional. Copied!

Is null a type in TypeScript?

Introduction to TypeScript Nullable. TypeScript Nullable is a special type null that has the value null. TypeScript Null is much like void, i.e. not useful on its own. By default, null is a subtype of all other subtypes which means a user can assign null to any of the data types like string, number, etc.

What is the difference between nullable and non nullable?

Nullable variables may either contain a valid value or they may not — in the latter case they are considered to be nil . Non-nullable variables must always contain a value and cannot be nil . In Oxygene (as in C# and Java), the default nullability of a variable is determined by its type.


2 Answers

As of TypeScript 2.1, you can use a lookup type to access an interface property.

IncomingMessage['url'] // string | undefined

You can combine that with NonNullable to fit your use case.

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

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

like image 75
Casey Woolfolk Avatar answered Sep 23 '22 10:09

Casey Woolfolk


So I found a solution which is slightly less hacky.

TypeScript 2.0 also has added a non-null assertion operator: !

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

In my case, it's still a bit annoying, since there are many different files which need to access this property and so this non-null assertion is used in many places.

like image 29
nickf Avatar answered Sep 22 '22 10:09

nickf