Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
staeke opened this issue Jan 2, 2017 · 6 comments
Closed

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

staeke opened this issue Jan 2, 2017 · 6 comments

Comments

@staeke
Copy link

staeke commented Jan 2, 2017

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
@zpdDG4gta8XKpMCd
Copy link

this requires subtraction types #4183

@mhegazy
Copy link
Contributor

mhegazy commented Jan 5, 2017

Not sure i see why this requires subtraction types..

@mhegazy
Copy link
Contributor

mhegazy commented Jan 5, 2017

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.

@staeke
Copy link
Author

staeke commented Jan 6, 2017

Thanks @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?

@masaeedu
Copy link
Contributor

masaeedu commented Apr 5, 2017

@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?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 2, 2018

This should be doable now with NonNullable type.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants