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

Proposal: Supplementing Implicit Types #19550

Closed
14 of 15 tasks
ricklove opened this issue Oct 28, 2017 · 7 comments
Closed
14 of 15 tasks

Proposal: Supplementing Implicit Types #19550

ricklove opened this issue Oct 28, 2017 · 7 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@ricklove
Copy link

ricklove commented Oct 28, 2017

In a type expression, I would like to be able to extend the implicitly known type of a value with specific type information.

The purpose is to get the most from the implicit type information and suplement it when some type information is missing.

Also, this will pull typescript even closer to ES6 by allowing common ES6 patterns like argument destructuring which results in the any type (where the type cannot currently be specified).

Syntax

// Like normal type information
const { req, opt = '', def = 'DEFAULT' }: { req: string, opt?: string, def?: string } = values;

// But 'extends' implicit typing
const { req, opt = '', def = 'DEFAULT' }: extends { req: string } = values;

The extends keyword would only modify the type information for the part of the type expression that was specified.

Useful Cases

Argument Destructuring with Type Information

Refer to: #7576 (comment)
And my comment there: #7576 (comment)

Full Typing (Absolutely no advantage from implicit type information)

export const Fun = ({ req, opt = '', def = 'DEFAULT' }: { req: string, opt?: string, def?: string }) => {
    // ...
};

Current Best Solution

This uses Pure Implicit Typing, but it requires a default parameter that allows an unintended invalid call.

export const Fun = ({ req, opt = '', def = 'DEFAULT' } = { req: '' }) => {
	// GOOD: All variables are implicitly typed
	// ...
};

export const Fun_Call = () => {
    // BAD: It is possible to call without arguments (i.e. pseudo-required)
    Fun();
    // GOOD: But if any object is given, it works right
    Fun({ req: '' });
};

With Extends

export const Fun_extends = ({ req, opt = '', def = 'DEFAULT' }: extends { req: string }) => {
    // ...
};

export const Fun_Call = () => {
    // GOOD: This is not allowed
    // Fun();
    // GOOD: An object must be given with the correct typing
    Fun({ req: '' });
};

Extending Return Values

Current Best Solution

// Complex Objects:
const ComplexReturn = () => { return { a: 'a', b: 'b', c: 'c' }; };

const obj = ComplexReturn();
const objWithD_typeofWithObject = obj as typeof obj & { d: string };

With Extends

// Complex Objects:
const ComplexReturn = () => { return { a: 'a', b: 'b', c: 'c' }; };

const objWithD_extends = ComplexReturn() as extends { d: string };

Checklist

Syntactic

  • What is the grammar of this feature?
    • 'extends' at the beginning of type expression like 'typeof' or 'keyof'
  • Are there any implications for JavaScript back-compat? If so, are they sufficiently mitigated?
    • No
  • Does this syntax interfere with ES6 or plausible ES7 changes?
    • No

Semantic

  • What is an error under the proposed feature? Show many examples of both errors and non-errors
    • The keyword would work in any type expression, just like 'keyof'
  • How does the feature impact subtype, supertype, identity, and assignability relationships?
    • For inheritance, it could be used to extend class members from their supertype
  • How does the feature interact with generics?
    • It would not change how the keyword is used with generics

Emit

  • What are the effects of this feature on JavaScript emit? Be specific; show examples
    • This is pure typing, it would be removed from runtime emit
  • Does this emit correctly in the presence of variables of type ‘any’? Features cannot rely on runtime type information
    • N/A
  • What are the impacts to declaration file (.d.ts) emit?
    • The keyword would need to by included in the declaration file typing information
  • Does this feature play well with external modules?
    • Yes

Compatibility

  • Is this a breaking change from the 1.0 compiler? Changes of this nature require strong justification
    • Unknown
  • Is this a breaking change from JavaScript behavior? TypeScript does not alter JavaScript expression semantics, so the answer here must be “no”
    • no
  • Is this an incompatible implementation of a future JavaScript (i.e. ES6/ES7/later) feature?
    • no

Other

  • Can the feature be implemented without negatively affecting compiler performance?
    • Yes
  • What impact does it have on tooling scenarios, such as member completion and signature help in editors?
    • It will provide a new type for that type expression
@ricklove ricklove changed the title Suggestion: Supplementing Implicit Types Proposal: Supplementing Implicit Types Oct 28, 2017
@aluanhaddad
Copy link
Contributor

This is definitely the most interesting solution to the destructuring arguments typing problem that I've seen proposed.

@ricklove
Copy link
Author

ricklove commented Oct 30, 2017

@aluanhaddad Cool!

I don't know how the typescript code-base or how it works in the engine.

Is it easy to access the known type at the time the type expression is being processed?

If the known type is already available, I would think it would be easy to accomplish what I described (extending the known type with a supplemental description).

Some other points:

I think this pattern would be preferable to a union in many cases. (Whenever it is desired to modify the type of a member instead of resulting in a union with the existing member type).

It also compliments Mapped types well. Mapped types affect all members, whereas this could affect only targeted members.

@mhegazy mhegazy added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Oct 30, 2017
@ricklove
Copy link
Author

Any feedback on this concept? Is there a better place to promote this idea?

@tjpalmer
Copy link

tjpalmer commented Mar 9, 2019

The in-place typing is shorter, easier to think through, and less repetitive. They closed this idea out before, perhaps over syntax, but I don't see why is can't be used.

// Previous example
const { req, opt = '', def = 'DEFAULT' }: extends { req: string } = values;
// Just simple in-place typing
const { req is string, opt = '', def = 'DEFAULT' } = values;

@ricklove
Copy link
Author

ricklove commented Mar 9, 2019

@tjpalmer Yeah, that could work for function parameters.

However, I would think “as” would make more sense because “as” indicates type coercion, whereas “is” indicates type comparison.

@tjpalmer
Copy link

tjpalmer commented Mar 9, 2019

I suggested is because they've previously rejected as for fear of potential future conflicts with ECMAScript. Some people would like to change destructure renaming to similar syntax as import renaming. Haven't seen it on the tc39 proposal list, but that's still been a concern on the TypeScript team.

@ricklove
Copy link
Author

ricklove commented Mar 9, 2019

Ah, yeah I think ECMAscript destructured renaming is the worst design decision I have seen in modern languange design (especially when they knew very well that this would fly in the face of TypeScript).

Like what were they thinking...

‘’’
// Since everybody understands this well
const x = {
a : “value”,
b: “value”,
};

// And this is clear
const {a,b} = x;

// And we need Default values
const { a, b, c=42 } = x;

// What about renaming?

// This would be too easy
const { a2:a, b2:b} = x;

// Oh, I know, let’s just reverse them because it makes sense - after all we have exactly no examples in the entire languange where a value assignment flows from left to right. So let’s just go in reverse from expected order for no good reason at all.
const { a:a2, b:b2} = x;

// Not only will this send a clear sign to TypeScript that we can do whatever we feel like, it will bring back the good old days when everyone made fun of horrible JavaScript design decisions.
‘’’

I don’t think that was the exact thought process, but I just don’t get it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants