Skip to content

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

New issue

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

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

Already on GitHub? # to your account

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.

# for free to subscribe to this conversation on GitHub. Already have an account? #.
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