-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Description
Omitting prototype from keyof
export declare class SomeClass {
static readonly FOO = "FOO";
static readonly BAR = "BAR";
// prototype: SomeClass;
}
export type ValueTypesOfClass = typeof SomeClass[keyof typeof SomeClass];
// This assignment should NOT be allowed, but it currently is in TypeScript.
const icon: ValueTypesOfClass = "hiiii";- What's wrong here?
- Grabbing all of the types of values on
typeof SomeClass- but that expands out to(typeof SomeClass)["prototype"] | "FOO" | "BAR". - But
(typeof SomeClass)["prototype"]is justSomeClass, which is an empty instance type. - So it's really just
{} | "FOO" | "BAR", and everything non-nullish is assignable to{}... - Which means that
"hiiii"is assignable toValueTypesOfClass
- Grabbing all of the types of values on
- Pattern is very dubious - could've written an enum here.
- Why get rid of it? It may break existing code.
- Conclusion
- Must check out Mongoose break. Believe it's an asymmetry between how we handle
keyofand how we omit properties in mapped types. Want to see how fixing that up changes things. - But also, we want to be cautious. Very niche. Skeptial that it adds value, whereas there's always a risk of breaking.
- Must check out Mongoose break. Believe it's an asymmetry between how we handle
Making const Contexts/Type Parameters Easier for Arrays
declare function f1<const T extends unknown[]>(arr: T): T;
declare function f2<const T extends readonly unknown[]>(arr: T): T;
const a = f1([1, 2, 3]);
// ^? const a: unknown[]
const b = f2([1, 2, 3]);
// ^? const b: readonly [1, 2, 3]-
For
f1we inferunknown[]because it omittedreadonly.- Why?
constpushes the type of the array towardsreadonly [1, 2, 3]- but that's not assignable tounknown[].Arrayhas methods for mutation, whereasReadonlyArraydoes not.
-
Very annoying footgun - today, it makes no sense to write
consttype parameters with mutable array constraints in TypeScript. -
Newest change pushes
const-y element types without violating the expectations of the contextual type. In this case, it doesn't push thereadonly-ness of the tuple as a whole. -
Note the change isn't simply restricted to
consttype parameters - it also works withas constwhen an outer contextual type has a mutable array type - which means thataandbhere are equivalent:declare function f1<const T extends unknown[]>(arr: T): T; const a = f1([1, 2, 3]); // ^ [1, 2, 3] // ^ // These work the same now! // v declare function f2<T extends unknown[]>(arr: T): T; const b = f2([1, 2, 3] as const); // ^ [1, 2, 3]
-
Does this step on
satisfies?- No, in fact we think it composes better. You can do
as const satisfies unknown[].
- No, in fact we think it composes better. You can do
-
It does break certain codebases which currently have a union of mutable/immutable tuple types. We think they can typically adapt the code fairly easily.
-
The most embarrassing part of the blog post introducing
consttype parameters was pointing out that you should never writeconsttype parameters with mutable array constraints - but that they were allowed to be written. -
Overall it feels right. Want to push on this direction.
Dynamically Named/Instantiable Tuple Labels
type SomeString = "world";
type Tuple = [`hello${SomeString}`: number]
// type Tuple = [helloworld: number]- Thought we understood this, took a closer look.
- Why is it such a crucial need to relabel the parameters?
- Seems dubious.
- Implementation feels strange - the thing that instantiates and follows with the type, but disappears as soon as have a union.
- Huh...
type SomeString = " world";
type Tuple = [`hello${SomeString}`: number]
// type Tuple = [hello world: number]-
Oh no...
type SomeString = ": number"; type Tuple = [`hello${SomeString}`: number] // type Tuple = [hello: number: number]
-
OH NO.
type SomeString = ": number, yadda"; type Tuple = [`hello${SomeString}`: number] // type Tuple = [hello: number, yadda: number]
-
We know, we know this is a prototype. 😄 You'd need some escaping to handle those.
-
That's not the only reason we're iffy on this.
-
Can almost imagine wanting to support labels from
const [count, setCount] = useState(0);- But that doesn't work. You'd need
const [count, setCount] = useState<"count">(0); - Suggesting a type parameter for something entirely design-time feels a bit ridiculous.
- But that doesn't work. You'd need
-
Doesn't feel consistent with names in other positions - expression vs. types.
-
Don't like how you lose the name if you have a union - doesn't feel like it's consistent with the rest of the type system.
-
Feels like the code you'd need to write for this would be so gross it's not worth the feature.
-
@weswigham had alternative approach to the use-case in the original issue that doesn't even need dynamic names for tuples.
-
When it comes to design-time validation and help, open-ended string completions are something we should probably solve first.
-
Conclusion:
- Number one thing is to better understand scenarios - we don't feel ready to commit to this until we see more motivating use-cases, code that this PR allows you to write that is compelling.
- Skeptical of approach for implementation. Stuff to fix if we wanted the feature:
- Syntax - make it consistent with where types are used over expressions in other positions. @weswigham to weigh in on this one.
- Disappearing in union case - feels very odd for the rest of the type system. Not clear on what could change there.
- [[Editor's Note: I don't know if that's a feature issue, but very surprising.]]