Skip to content

Allow more constructs to work as type guards for unknown #25720

Open
@AlCalzone

Description

@AlCalzone
Contributor

Search Terms

unknown type guard
Related: #24439 (comment), #25172

Suggestion

Currently, only a very limited set of type guards are able to narrow the new unknown type:

  • Array.isArray (because it is a typeguard for arg is any[]) and probably some more in the lib files
  • instanceof
  • self-written typeguards

However to make working with unknown types less awkward, I'd like to see a couple of other constructs being able to narrow the unknown type:

let x: unknown;
// Direct equality should narrow to the type we compare to
x === "1"; // should narrow x to string or the literal "1" type, similar for other types aswell

// All these should narrow x to {prop: any}
"prop" in x;
x.prop != null;
x.prop !== undefined;
typeof x.prop !== "undefined";

// typeof should work on properties of the unknown variable
typeof x.prop === "string"; // should narrow x to {prop: string}

Use Cases

Make unknown easier to work with!

// before, very verbose!
const x: unknown = undefined!;

function hasProp1(x: any): x is {prop1: any} {
	return "prop1" in x;
}
function hasProp2(x: any): x is {prop2: any} {
	return "prop2" in x;
}
// imagine doing this for many more properties

if (hasProp1(x)) {
	x.prop1;
	if (hasProp2(x)) {
		x.prop2;
	}
}

// ===========

// after, much more concise and less overhead
const x: unknown = undefined!;
if ("prop1" in x) {
	x.prop1;
	if ("prop2" in x) {
		x.prop2;
	}
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
    This wouldn't change the runtime behavior of existing JavaScript code
    This could be implemented without emitting different JS based on the types of the expressions
    This isn't a runtime feature (e.g. new expression-level syntax)

Activity

mattmccutchen

mattmccutchen commented on Jul 17, 2018

@mattmccutchen
Contributor

The use of in is covered by #21732. Shall we add the other checks for a property (comparison to undefined, typeof) to #21732 and call this a duplicate of #21732 + #25172?

AlCalzone

AlCalzone commented on Jul 17, 2018

@AlCalzone
ContributorAuthor

Sure, why not!

added
SuggestionAn idea for TypeScript
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this feature
on Jul 17, 2018
AlCalzone

AlCalzone commented on Jul 19, 2018

@AlCalzone
ContributorAuthor

And one more thing:

let foo: unknown;
if (typeof foo === "object") {
	// foo should probably be narrowed to {[prop: string]: unknown} here
}
ghost

ghost commented on Jul 24, 2018

@ghost

At the least, at typeof foo === "object" it should narrow to object. Currently remains unknown and the following fails:

function f(u: unknown): object { return typeof u === "object" ? u : {}; }
mhegazy

mhegazy commented on Jul 27, 2018

@mhegazy
Contributor

Similar requests in #10715, #25172, and #21732

simonbuchan

simonbuchan commented on Aug 21, 2018

@simonbuchan

I was hoping unknown would let me have type-safe data-loading, e.g.:

interface SomeValue { a: string, b?: number, c: 'left' | 'right' }
function readValue(id: string): SomeValue {
  const u: unknown = await someDataSource(id);
  if (
    typeof u !== 'object' ||
    u === null ||
    typeof u.a !== 'string' ||
    b in u && typeof u.b !== 'number' ||
    u.c !== 'left' && u.c !== 'right'
  ) {
    throw new Error(util.format('Invalid value with id %O from some data source: %O', id, u));
  }

  return value; // TS is checking that the checks above actually verify the return type here
}

This to me would be a better match to TS for what #26078 wants, but I wouldn't complain about adding quick-fixes to add the missing checks!

(remember that typeof u === "object" should actually narrow to object | null - Thanks javascript!)

w0rp

w0rp commented on Sep 12, 2018

@w0rp

I would like it if type guards with unknown worked a little more like this.

let x: unknown

if (typeof x === 'object' && x !== null && 'foo' in x && typeof x.foo === 'string') {
  /* x is promoted to {foo: string} here */
}

I think the type promotion ought to work like so, if at all possible.

  1. typeof unknown === 'object' -> object | null
  2. (object | null) !== null -> object
  3. 'foo' in object -> {foo: unknown}
  4. typeof {foo: unknown}.foo === 'string' -> {foo: string}

22 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @butchler@jcalz@AndreasGassmann@rattrayalex@simonbuchan

        Issue actions

          Allow more constructs to work as type guards for `unknown` · Issue #25720 · microsoft/TypeScript