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

keyof union type should produce union of keys, not intersection of keys #12948

Closed
blakeembrey opened this issue Dec 15, 2016 · 5 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@blakeembrey
Copy link
Contributor

TypeScript Version: 2.1.4 and 2.2.0-dev.20161215

Code

function test<T>(data: T, keys: (keyof T)[]) { }

interface User {
  username: string
  email?: string
}

test<User>({ username: '' }, ['email'])
test<User | { foo: string }>({ username: '' }, ['email'])

Expected behavior: No error.

Actual behavior: Error as keyof T is never[] instead of ("username" | "email" | "foo")[].

Possibly related to #12815, #10256 and #8896, except this is from the key side. It makes it really hard to use pick on any unions.

@ahejlsberg
Copy link
Member

This isn't safe for the same reason as #12815.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Dec 15, 2016
@blakeembrey
Copy link
Contributor Author

blakeembrey commented Dec 15, 2016

@ahejlsberg Can you elaborate on why that is, because the reason of the types in #12815 (comment) is not valid when we're only getting the keys.

For more context, I have a database query that accepts PublicGroup | PrivateGroup and does an upsert. In this case, I want to write the keys that should update on upsert explicitly and it happens that they exist on one member of the union but not the other (so I can do a pick and only send the document properties I need to on update). The full signature looks more like this but currently isn't possible to do:

export async function createOrUpdateSecondary <In, Out extends Dates> (
  tableName: string,
  data: In,
  key: r.r.StringLike<string> | r.r.NumberLike<number>,
  index: string,
  updateKeys: (keyof In)[],
  filterBy?: (row: r.RDatum<Out>) => r.RBoolean<boolean>
): Promise<Out> {}

Edit: I can see it should be invalid if you use that to select the key that may not exist for the reasons in #12815, but in type positions shouldn't it be valid as without it, you can't actually model pick properly without this feature.

Edit 2: Also, if we needed to narrow the keys available on both sets, we could do that ourselves using keyof In & keyof Out.

@blakeembrey
Copy link
Contributor Author

Also, if we had exact types (#12936) would this change any opinion on how properties of a union can be operated on?

@ahejlsberg
Copy link
Member

ahejlsberg commented Dec 15, 2016

A core assumption we make is that T[K] is a permitted and type-safe operation when K is keyof T or a type parameter constrained to keyof T. This would no longer be true with what you propose because T[K] could fetch some completely unknown value if T is instantiated with a union type (for the reason discussed in #12815).

The picture changes with exact types, both for regular property access and keyof. Basically we'd say that a union of exact types has a union of the property names in the constituent types, with undefined added to the types of the properties that aren't present in every constituent.

@blakeembrey
Copy link
Contributor Author

Thanks. I think I understand the usage of T[K] part, just not the part where we could have an expanded K on unions that is later narrowed before usage somehow (but I guess there's no way to narrow it today anyway, until subtraction types). I'll keep an eye out and maybe see what I can do for exact types as that sounds like the best solution, thanks.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants