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

Treat assignments to properties on functions as valid declarations #15868

Closed
DanielRosenwasser opened this issue May 16, 2017 · 3 comments
Closed
Assignees
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented May 16, 2017

Background

Something that newcomers run into as a result of migrating to TypeScript is that certain patterns don't work quite as easily anymore. For example, let's consider the following example:

export const Foo = props => <div>{props.name}</div>

Foo.propTypes = {
    // ...
};

This example doesn't work in TypeScript because propTypes isn't a valid property on Foo's type.

In the above case, there are two potential workarounds:

  1. Find/declare a type that exists that is callable and has a valid property:

    export const Foo: StatelessComponent<any> = props =>
        <div>
            {props.name}
        </div>
    
    Foo.propTypes = {
        // ...
    };

    This option has the benefit of being able to statically catch incorrectly named declarations, but is somewhat unattractive because of the placement. It also doesn't work well if the original declaration was a function declaration like so:

    export function Foo(props) {
        return <div>props.name</div>
    }

    Because it would need to be rewritten to a variable declaration to allow an annotation.

  2. Rewrite the const declaration to a function declaration and use namespace merging:

    export function Foo(props) {
        return <div>props.name</div>
    }
    
    namespace Foo {
         export let propTypes = { /*...*/ };
    }

    This option takes advantage of the namespace construct in TypeScript which performs value-merging; however, it is potentially worse because

    1. It requires the use of a construct that is fairly TypeScript specific, which may be unattractive to newcomers.
    2. It only works for function declarations. This requires a rewrite from any const declarations.

Unless there's a new construct which creates a callable object (cc @bterlson), then I want to discuss the option of recognizing property assignments on functions. In other words, the original example would simply be something that TypeScript could understand.

Proposal

For an effective function declaration named F at the top-level scope, the first assignment to a property p on F (i.e. F.p) is considered a declaration of a property named p on the type of F.

An effective function declaration can be described by a FunctionDeclaration or a VariableDeclaration whose initializer is

  1. a FunctionExpression
  2. an ArrowFunction
  3. a series of arbitrarily nested ParenthesiezdExpressions whose innermost expression is either of the above

Precedent

We already do this to some extent in checkJs mode because we simply have to. Not doing this would lead to an unacceptable experience there.

Drawbacks

  1. These declarations are significantly less explicit than other declarations, which makes the language less consisent.
  2. Type annotations cannot be placed on these declaration sites, whereas all other variable/property declarations allow a type annotation.
  3. Users may be confused by the positions where these declarations can occur.

Alternative Ideas

  1. Augmented variable declaration syntax

    let Foo.propTypes = { /*...*/ };
  2. Allow namespaces to merge with const/let/var declarations.

    export const Foo = props => <div>{props.name}</div>;
    export namespace Foo {
        export let propTypes = { /*...*/ };
    }
  3. Find a way to cleanly coerce the type of a function declaration.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 11, 2017

Covers #7632, #9474, and #10868

@Jessidhia
Copy link

My proposal at #16918 seems related to this issue; at least where the contextual type only has optional properties.

@RyanCavanaugh RyanCavanaugh added Committed The team has roadmapped this issue and removed In Discussion Not yet reached consensus labels Oct 30, 2017
@RyanCavanaugh
Copy link
Member

Approved for function declarations and consts initialized with a function expression; only applies when the declaration and the property write occur in the same containing function (or global)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants