-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Closed
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Description
TypeScript Version: 3.1.0-dev.201xxxxx
Search Terms:
- keyof generic intersection
Code
Helper types:
// Extract keys whose values match a condition
type Filter<T, Cond> = {
[K in keyof T]: T[K] extends Cond ? K : never
}[keyof T]
type Fun = (...args: any[]) => any
// Extract method names from an object type
type Methods<T> = Filter<T, Fun>
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Prefer<T, U> = U & Omit<T, (keyof T) & (keyof U)>
Implementation:
interface Events {
foo(): void
bar: number
}
class Foo<T, U = T & Events> {
hey(arg: Methods<U>): void { }
duk(arg: Filter<U, Fun>): void { }
wat(arg: Methods<T & Events>): void { }
how(arg: Filter<T & Events, Fun>): void { }
foo(arg: Methods<Prefer<T, Events>>): void { }
who(arg: Filter<Prefer<T, Events>, Fun>): void { }
ugh(arg: keyof U): void { }
bar(arg: keyof Prefer<T, Events>): void { }
tau(arg: keyof (T & Events)): void { }
test() {
this.hey('foo') // ✕
this.duk('foo') // ✕
this.wat('foo') // ✕
this.how('foo') // ✕
this.foo('foo') // ✕
this.who('foo') // ✕
this.ugh('foo') // ✕
this.bar('foo') // OK ("foo" | "bar" | Exclude<keyof T, ("foo" & keyof T) | ("bar" & keyof T)>)
this.tau('foo') // OK (keyof T | "foo" | "bar")
}
}
Expected behavior:
No errors
Actual behavior:
Unexpected errors
Playground Link: https://goo.gl/X4KcBN
Metadata
Metadata
Assignees
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
RyanCavanaugh commentedon Sep 24, 2018
It looks like you're misapprehending what
=
does - in<T, U = T & Events>
,U
has a default ofT & Events
but is in no way constrained, e.g.new Foo<string, boolean>
is OK and you shouldn't expect an arbitraryU
inside your class to haveEvents
keys.aleclarson commentedon Sep 24, 2018
@RyanCavanaugh Yeah, sorry about that. But adding
extends Events
before the=
operator only fixes the last error. Why are the others still errors?RyanCavanaugh commentedon Sep 24, 2018
It's not correct to eagerly try to evaluate a conditional type when one of its inputs is generic - certain instantations of
T
actually would change what the correct outputs ofMethods
are.aleclarson commentedon Sep 24, 2018
You'd think
Methods<U>
could be partially evaluated to'foo' | Methods<T>
. Why aren't the non-generic parts of an intersection able to be evaluated?RyanCavanaugh commentedon Sep 24, 2018
This presumes to know that nothing from
Methods<T>
could intersect in something into thefoo
property to cause it to become unassignable to(...args: any[]) => any
. We know from inspection that this can't happen because you can't add anything to(...args: any[]) => any
to cause it to become an incompatible target, but the compiler can't reason about types on a meta-level like that.aleclarson commentedon Jan 14, 2019
As of TypeScript v3.2, only the first 2 function calls cause compile errors. 🎉