Skip to content

Suggestion: Type assertion operator (!) with mapped types #13253

Description

TypeScript Version: 2.1.1

Also relates to #12424

I recently spent some time looking at the discussion on a safe navigation ?. operator in typescript/javascript (see #16, and https://esdiscuss.org/topic/existential-operator-null-propagation-operator). Then it struck me that ES6 proxies could be a solution that didn't require new language operators. Wrote something working together, and also found this existing npm package: https://www.npmjs.com/package/safe-proxy. In summary, you'd be able to write

// Safe (only does actual property access if underlying object is valid)
safe(a).b.c().d[0]

// ...instead of the functionally equivalent but unsafe
a.b.c().d[0]

Now, how do I type annotate this? At first, I thought maybe the new mapped types in Typescript 2.1 could be used, but then I've come to believe that it isn't possible currently (please let me know how to do this otherwise). What I miss is a generalized way of narrowing optional types under ---strictNullChecks.

For example, let's consider using the mapObject from the the Typescript 2.1 release wiki writeup, with optional properties:

const names:{ foo: string, bar?: string} = { foo: "" };
mapObject(names, s => s.length)

This results in a TS2345 error (basically bar can't be optional)

Now, back to the SafeProxy type. The best I can come up with today is

type SafeProxy<T> = {
    [P in keyof T]: SafeProxy<T[P]&any>;
};

...which is marginally better than just any.

I would want (and need for my example above) to define something like below. NOTE: the assertion operator here is made-up-syntax. I suggested an exclamation mark after the actual type, but this could obviously be debated.

type SafeProxy<T> = {
    [P in keyof T]: SafeProxy<T[P]!>;
};

Interestingly it would kind of enable the reverse of Partial<T>:

// existing in lib.d.ts
type Partial<T> = {
    [P in keyof T]?: T[P];
};

// potential
type Complete<T> = {
   [P in keyof T]: T[P]!;
}

Side note: As I intend the SafeProxy class to implement Symbol.toPrimitive, no trailing __value or similar would be needed. This seems to play well with typescript primitives though

Some other usages (that I guess all play pretty well with --strictNullChecks)

  • advanced assertion style functions
  • default mechanism functions

Activity

  1. zpdDG4gta8XKpMCd commented on Jan 2, 2017

    @zpdDG4gta8XKpMCd

    this requires subtraction types #4183

  2. mhegazy commented on Jan 5, 2017

    @mhegazy
    Contributor

    Not sure i see why this requires subtraction types..

  3. mhegazy commented on Jan 5, 2017

    @mhegazy
    Contributor

    If safe was one level only proxy, you should be able to use generic type inference to get the non-optional version of your type. You can achive this by type safe as:

    declare function safe<T>(o: Partial<T>): T;
    
    var a: { b?: string, a?: number } = {};
    var x = safe(a); // {b:string, a: number};

    you could even add support for null:

    type PartialOrNullable<T> = {
        [P in keyof T]?: T[P] | undefined | null;
    };
    declare function safe<T>(o: PartialOrNullable<T>): T;
    
    var a: { b?: string, a: number | null };
    var x = safe(a); // {b:string, a:number}

    with #12596 implemented, you can even allow dotting off unknown properties if you add a string indexer to the output:

    declare function safe<T>(o: PartialOrNullable<T>): T & {[x:string]: any};
    
    
    var x = safe(a);
    x.unkown // allowed with 12596, and its type would be any;

    for the recursive version of it, #12769 tracks fixing it.

    In general ! type operator is useful, but you can use generic type inference to get the same scenario achieved.

  4. staeke commented on Jan 6, 2017

    @staeke
    Author

    Thanks Mohamed Hegazy (@mhegazy) for a great reply. I realize that it's a truly useful and powerful concept to stop thinking of type parameters as "the input type", and more like "the common denominator between inputs/outputs". Reminds me of Type used in Angular to denote the "type of the constructor". Maybe worth adding something similar to the documentation around Partial?

  5. masaeedu commented on Apr 5, 2017

    @masaeedu
    Contributor

    Mohamed Hegazy (@mhegazy) Can this be done without declaring and implementing a function? If I have a partial type T, how do I simply declare a variable of the "un-partialed" type?

  6. mhegazy commented on Mar 2, 2018

    @mhegazy
    Contributor

    This should be doable now with NonNullable type.

  7. locked and limited conversation to collaborators on Jul 3, 2018
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions