Repository navigation
Design Meeting Notes, 6/5/2020 #38951
Description
unknown annotation on catch clause variables
- If you can write
unknown, can you writeany?- Yes
- Can you write
never?- No, you can't guarantee that nothing will ever throw.
- Types that resolve to
unknownandany?- Sure
Placeholder Types
Thanks to Orta Therox (@orta) for helping with the notes here
Background: we've talked about placeholder types, the idea is you need to talk about a type which exists even though you don't know all of the contents before you use it.
Normally that's an issue WRT conflicting environments. You can do an interface but they have flaws like
interface Buffer {}
let x: Buffer
function foo(x:Buffer) {}
// all work though :(
foo(1)
foo("")
foo({})A proposal:
placeholder type Foowhich is verbose for searchability, it can be declared but not assigned
placeholder type Foo
const a: Foo = 12 // errorFails! You need to have an implementation
placeholder type Foo
type Foo = number
const a: Foo = 12Now it's legal.
You can constrain the placeholder:
placeholder type Foo extends string
// fails
const a: Foo = 12and you can have many constraints which are intersected:
placeholder type Foo extends string
placeholder type Foo extends number
// fails because foo is a never
const a: Foo = 12- Is this bascially an existential type? Is this a new type kind
Ish. It needs to act like a type param in assignability. Needs to be able to do the same circularity checking for type aliases and type parameters.
Has to have all the same interface validation too. Because it does all that, it needs to anticipate being a class, interface or type - it needs to encode a lot of different data.
So I opted for a new 'type kind' - which is
- What's TODO?
Generics is tricky (higher kinded types ish)
placeholder type foo<T> extends stringCould this be figured out at bind time? And at check time we know if there's an implementation. Can it be resolved before figuring out where it is about it?
- That's already what it does.
Foo isn't higher kinded, but once resolved it is - you can know whether there was an implementation or not post bind-time.
placeholder type Foo<T> extends { x: T }If an implementation exists, you can discard all placeholder resolution logic and just perform consistency checks on the declarations.
Lots of people want to bikeshed on the name placeholder - maybe defer?
Q: How does this relate to placeholder types for values
Think some of those ideas could be re-used
placeholder const x
if (typeof x === "")Or examples:
placeholder type SetTimout
declare const coolSetTimeour: NodeSetTimeout
declare const coolSetTimeour: DOMSetTimeoutQ: What kind of problems does this solve?
Lots of deps have a hard dep on Node.js types (often for Buffer) - instead of including all of Node.js (and changing your entire environment)
then you can instead declare "I use buffer" and your package does not need to include all of types for environment
placeholder type Buffer;
placeholder type Map<K, V>;
placeholder type HTMLElement;are real-world cases across ImmutableJS, Vue, etc.
Q: How do we deal with a world where people keep moving things into the global scope, can I do a placeholder module?
e.g. something like?
placeholder module 'showdown' {
export function parse(): ShowdownDocument;
export placeholder type ShowdownDocument;
}You also need placeholder values (this module has this value which has this type). Are we making higher order namespaces?
// Maybe?
placeholder type "buffer".Buffer Note: Buffer is both exported by node global and a buffer types, and the global is deprecated
Q: Do We want a way to assert a module has a specific type?
Idea: We want to declare a type alias which defaults to something. Today you can only have one, but it'd be great to have multiple.
- but it's not a type alias, it has to account for more than just that
Some of the implementation details for this can be done offline.
Variadic Tuples
-
Current work: can now have any number of generic placeholders in the middle of a tuple
type Foo<T extends any[]> = [number, ...T, string];
-
If you instantiate one of these with an un-fixed array type, the type unions with subsequent element types to create a rest type.
type Bar = Foo<boolean[]>; // [number, ...(string | boolean)[]]
-
With some inference smarts, can now write
type First<T extends readonly[]> = T[0]; type DropFirst<T extends readonly[]> = T extends [...any[], infer U] ? U : never;
-
Inference rules make sense, but get hairy.
- More to come here.
Open String Union Types
interface Options {
borderColor: "black" | "red" | "green" | "blue" | "yellow" | string;
}- People just want completions, but you can assign any string.
- But what about
boolean | number | any- same deal?keyof
- Very similar idea to proeprties + index signatures. Nonsensical, but
keyofon that thing makes sense. - If it's completions-only, maybe JSDoc?
- But how do you preserve type identity?
stringis the samestringno matter where you use it.- There is no special "
string, but this one with auto-complete"
- We have something pertinent where we stop doing subtype reduction in certain cases.
- We could avoid doing subtype reduction until comparison time?
out of time
Activity
treybrisbane commentedon Jun 6, 2020
Daniel Rosenwasser (@DanielRosenwasser) Very keen for this tuple work! 😍
Also, the point about placeholder types being higher-kinded-ish has definitely peaked my interest! Some notion of higher-kinded types could address a few of TypeScript's limitations, including things like #32523 and #26242. I'm not sure if placeholder types would help in either of those two cases specifically, but it's nice to see some thinking in the higher-kinded area. 🙂