GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Keysof type operator by weswigham · Pull Request #10425 · microsoft/TypeScript

Conversation

@weswigham

@weswigham Wesley Wigham (weswigham) commented Aug 18, 2016

Copy link
Copy Markdown
Member

Fixes half of #1295. (Honestly, that issue is like proposals for three separate but interoperating features) The remaining parts of #1295 are what I call a "type access expression" - a mirror of element access expressions, but for types (quite literally the same logic) - and altering index resolution logic to, rather than using unions to check for index signature compatibility, be able to use a union of string literals to create a union of the types of those members.

The part that this PR addresses is an operator for finding all of the keys of an arbitrary type. This PR adds a new "type operator" (akin to typeof) - keysof. Used like so:

interface Item<T> {
  owner: Person;
  id: number;
  contents: T
}

let x: keysof Item<any>;
x = "id"; // valid
x = "owner" // valid
x = "content" // error! "content" not assignable to keysof Item<any>

interface HelloWorld { hello: any; world: any; }

function check<K extends keysof HelloWorld>(key: K): K{
    return key;
}

// 'y' has type '"hello"'
let y = check("hello");
check("hullo"); // error! "hullo" not assignable to keysof HelloWorld

A parameter or return type being a keysof type causes it to be contextually types as a string literal if need be. A keysof type resolves to a union of the string literal types of the keys of the given type when it is actually resolved (which it is resolved to for relationship checking). It is StringLike, and as such it has the apparent type of a string. It is effectively sugar for writing out the union type of all the keys of an object by hand, except it is also able to be generically instantiated.

cc Daniel Rosenwasser (@DanielRosenwasser)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

wclr, Mr-Byte, vvakame, aluanhaddad, ethanresnick, Strate, ichpuchtli, dsherret, thorn0, GregOnNet, and 3 more reacted with thumbs up emoji jwbay, DanielRosenwasser, svieira, HerringtonDarkholme, alitaheri, joshaber, yortus, wclr, jkillian, bryanrideshark, and 9 more reacted with hooray emoji alitaheri, wclr, jkillian, Mr-Byte, blakeembrey, well1791, ethanresnick, Strate, aluanhaddad, remojansen, and 4 more reacted with heart emoji
All reactions
  • 👍 13 reactions
  • 🎉 19 reactions
  • ❤️ 14 reactions
Wesley Wigham (weswigham) added 2 commits August 18, 2016 15:41
@weswigham
keysof type operator
9613ca8
@weswigham
Condier keysof types string like, dont get index types
65ff3fd
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-already-signed label Aug 18, 2016
@weswigham
Actually accept baselines
b156f71
@weswigham
Wesley Wigham (weswigham) force-pushed the keysof-type-operator branch from 01b0744 to b156f71 Compare August 19, 2016 20:36
Wesley Wigham (weswigham) added 2 commits August 19, 2016 13:41
@weswigham
Add cache
bd8f8ff
@weswigham
I CANT EXPLAIN THIS
11dce6f
@alitaheri

Ali Taheri Moghaddar (alitaheri) commented Aug 20, 2016

Copy link
Copy Markdown

Also probably solves #7722.

How would classes and namespaces work with this? ( if they should in the first place 😅 )

and how about index signatures?

interface A {
  foo: string;
  [property: string]: any;
}

what will be keysof A look like? string ?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham

Wesley Wigham (weswigham) commented Aug 20, 2016

Copy link
Copy Markdown
Member Author

Ali Taheri Moghaddar (@alitaheri): keysof ClassName gets you the instance members, keysof typeof ClassName gets you the static members, no special handling required. Any ole' type is supported in the argument position (with the caveat that anonymous objects must be within parenthesis to disambiguate them from function bodies).

alitaheri and aluanhaddad reacted with thumbs up emoji alitaheri, aluanhaddad, and remojansen reacted with heart emoji
All reactions
  • 👍 2 reactions
  • ❤️ 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

DanielRosenwasser
DanielRosenwasser reviewed Aug 20, 2016
View reviewed changes
Comment thread src/compiler/checker.ts
if (!type.resolvedBaseUnion) {
// Skip any essymbol members and remember to unescape the identifier before making a type from it
const memberTypes = map(filter(getPropertiesOfType(type.baseType),
symbol => !startsWith(symbol.name, "__@")),

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Aug 20, 2016

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you split this into intermediate variables?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@basarat Basarat Ali Syed (basarat) mentioned this pull request Aug 21, 2016
keyof basarat/typescript-book#155
Open
@saschanaz Kagami Sascha Rosylight (saschanaz) mentioned this pull request Aug 25, 2016
Compile-time checking of string literal arguments based on type #394
Closed
@basarat Basarat Ali Syed (basarat) mentioned this pull request Aug 26, 2016
Flow type helpers #2710
Open
@gcnew Marin Marinov (gcnew) mentioned this pull request Sep 1, 2016
Dynamic function typings based on string params passed #10660
Closed
@wclr

Alex (wclr) commented Sep 1, 2016

Copy link
Copy Markdown

When this going to landed?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented Sep 1, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Renamed in favour of (@whitecolor) We need to discuss this at a design meeting. There are small details that can make all the difference (such as what to do with index signatures).

wclr and HerringtonDarkholme reacted with thumbs up emoji wclr and remojansen reacted with heart emoji
All reactions
  • 👍 2 reactions
  • ❤️ 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@wclr

Alex (wclr) commented Sep 1, 2016

Copy link
Copy Markdown

Daniel Rosenwasser (@DanielRosenwasser) it would be cool if another part for #1295 would be implemented soon

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Sep 9, 2016
Design Meeting Notes, 9/9/2016 #10826
Closed
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Sep 19, 2016
Typing the Index Signature of a Module Namespace Object #10998
Closed
@aluanhaddad

Aluan Haddad (aluanhaddad) commented Oct 1, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

I think this is a big step forward for the type system. The question I have, and sorry if this has already been answered, is this: What is the enumeration algorithm used?

  1. Is it the same as Object.keys?
  2. Is it the same as an unfiltered for..in?
  3. If neither of the above, how exactly does it work.

On the one hand, it could be very surprising to not get the property keys of super class properties, especially as subclassing becomes more and more fashionable. On the other, it would be equally surprising if it behaved differently from Object.{keys | entries | values}.

Personally, I would prefer it to only return enumerable own properties, but I almost never create class hierarchies in TypeScript.

GoToLoop reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented Oct 1, 2016

Copy link
Copy Markdown
Member

Aluan Haddad (@aluanhaddad) Problem is that enumerable own properties aren't encoded in the type system and there's not really a straightforward way we've thought of to add it. Nathan Shively-Sanders (@sandersn) ran into similar issues with the object rest & spread operators.

aluanhaddad reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@aluanhaddad

Aluan Haddad (aluanhaddad) commented Oct 1, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Daniel Rosenwasser (@DanielRosenwasser) thank you for clarifying.

It makes a lot of sense that the language does not track own properties because statically modelling the flexibility of JavaScript's composition mechanisms would seem to preclude doing so.

Perhaps if this operator walks the prototype chain a different name would be appropriate.

keysof is a really pleasant name, but if you read it aloud in a mathematical style it, sounds like calling Object.keys. I don't mean to be pedantic here, but it would be confusing if you use the latter frequently.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@yortus Troy Gerwien (yortus) mentioned this pull request Oct 24, 2016
Suggestion: design-time only propertiesOf operator #11813
Closed
@remojansen

Remo H. Jansen (remojansen) commented Oct 24, 2016

Copy link
Copy Markdown
Contributor

I created #11813 because I was not aware of this. I have closed it but it can still be used as a real world use case for the need of this operator.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@wycats

Yehuda Katz (wycats) commented Oct 25, 2016

Copy link
Copy Markdown

Aluan Haddad (@aluanhaddad) I can understand why you expect keysof and Object.keys to refer to the same concept (they both have the word "keys" in them, after all).

That said, I'd find it unfortunate if this very useful operator was restricted to enumerable-own properties.

Here's an example scenario I ran into today that got me thinking about this feature again: creating a hard-fail version of the index-for-friendly-access trick:

class X {
  constructor(private name: string) {}
}

let x = new X("Godfrey");
x['name'] // type checks, "Godfrey", type is string, as expected
x['nmae'] // type checks, undefined, type is any, unexpected

In other words, this trick conflates the elimination of privacy checks with a willingness to accept successful type-checking and any as a result if the property doesn't exist at all.

We could use the keysof operator to create a safer version of the same operator:

class X {
  constructor(private name: string) {}
}

let x = new X("Godfrey");
x['name'] // type checks, value is "Godfrey", type is string, as expected
x['nmae'] // type checks, value is undefined, type is any, unexpected

function get<T, K extends keysof T>(obj: T, key: K) {
  return obj[key];
}

let x = new X("Godfrey");
get(x, 'name') // type checks, "Godfrey", type is string (?), as expected
get(x, 'nmae') // fails to type check

Whether this works or not depends on whether keysof includes private or protected fields, and whether the type inferencer expands get correctly.

Regardless of those details, this illustrates that there may be valid use-cases for keysof working both against private/protected fields, against, non-enumerable fields, and against non-own fields.

My own perspective is that keysof should match the semantics of the no-implicit-any indexing operator: in other words, a key is in keysof (typeof val) if typeof val[key] is a type known to the type system. Pairing a keysof type check with the indexing operator seems eminently reasonable to me.

This might also argue for a few flavors of keysof, but matching semantics already encoded in the type system in some way seems like a pre-requisite for quick inclusion.

Finally, I also wonder whether not encoding enumerable/own in the type system will be practical in the long-term, especially considering the soon-to-be-included object-spread ({ ...a, ...b }) operator in JavaScript.

aluanhaddad reacted with thumbs up emoji aluanhaddad reacted with heart emoji
All reactions
  • 👍 1 reaction
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@blakeembrey Blake Embrey (blakeembrey) mentioned this pull request Oct 26, 2016
Implement subtraction and rest type once TS 2.1 lands types/rethinkdb#2
Open
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 28, 2016
Static types for dynamically named properties #11929
Merged
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Oct 30, 2016
Design Meeting Notes, 10/28/2016 #11943
Closed
@aluanhaddad

Aluan Haddad (aluanhaddad) commented Oct 30, 2016

Copy link
Copy Markdown
Contributor

Yehuda Katz (@wycats) I think you're points about overall usefulness outweigh my arguments about consistency with existing features. While I don't use classes particularly often I do find the object-spread example very compelling. In light of that feature, requiring ownness would kill a lot of the usefulness even in scenarios involving simple object literals. Also if manifest mixin support is ever added to ECMAScript, restricting it to enumerable own properties would also be very unfortunate.

I am convinced.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Nov 2, 2016

Copy link
Copy Markdown
Member

Closing as this has been superseded by #11929.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg Anders Hejlsberg (ahejlsberg) closed this Nov 2, 2016
@aluanhaddad Aluan Haddad (aluanhaddad) mentioned this pull request Dec 25, 2016
Implementation of inheritance not compatible with Object.getOwnPropertyNames() #13163
Closed
@weswigham
Wesley Wigham (weswigham) deleted the keysof-type-operator branch April 10, 2018 23:08
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

9 participants

@weswigham @alitaheri @wclr @DanielRosenwasser @aluanhaddad @remojansen @wycats @ahejlsberg @msftclas
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.