Typescript: Allow pass through of class-properties

Created on 13 Nov 2016  路  5Comments  路  Source: microsoft/TypeScript

TypeScript Version:

  • 2.2.0-dev.20161113 (Not version specific)
  • target = esnext, jsx = preserve

Description
I expect Typescript to more or less only strip types but preserve the rest - based on the target compilerOption. Especially when using a target like es2017 or even esnext.

Class-properties are always moved into the constructor. This prevents hot-reloading of class-property functions when using react-hot-loader 3.

Using them is a common pattern for binding event-handler functions to this without having to do this for every method in the constructor.

Code

class MyClass {
  prop1: number = 123;
  handleClick = () => {
    console.log('Click handled');
  }
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

Expected emit:

class MyClass {
  prop1 = 123;
  handleClick = () => {
    console.log('Click handled');
  }
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

Actual emit:

class MyClass {
  constructor() {
    super(...arguments);
    this.prop1 = 123;
    this.handleClick = () => {
      console.log('Click handled');
    };
  }
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}
Bug ES Next help wanted

Most helpful comment

I've observed a difference in the ESNext proposal spec for class properties and the current TypeScript class behavior. Consider this code:

class A {
    property = 'some string';
}
class B extends A {
    property;
}
const instanceB = new B();

It seems that, with the wording of the ES proposal, property gets redefined in B, so that instanceB.property === void 0. The babel implementation seems to call Object.defineProperty in the case where a modification must be made to an existing property descriptor.

It's certainly possible to make TypeScript do the same thing, but this change has significant risk of breaking existing code. This change would likely be observed by many users since TypeScript property declarations are not only used for runtime effect - but also to add type information. (Therefore, it's likely that many are actually using property declarations with no initializer.) See the following example:

class Greeting {
    greeting: string = 'generic greeting';
    greet() { console.log(greeting); }
}
class HelloGreeting extends Greeting {
    greeting: 'hello'; // Narrows type from string to "hello"
}
const greet = new HelloGreeting();
greet.greet();
// Current TS behavior: prints "generic greeting"
// ES proposal behavior: prints undefined

If TS is to stay consistent with ES, then a breaking version change will need to be made (with a flag to opt-in to the existing behavior?)

All 5 comments

We need to move the property initialization transformation to the esnext transform.

PRs are welcomed.

I've observed a difference in the ESNext proposal spec for class properties and the current TypeScript class behavior. Consider this code:

class A {
    property = 'some string';
}
class B extends A {
    property;
}
const instanceB = new B();

It seems that, with the wording of the ES proposal, property gets redefined in B, so that instanceB.property === void 0. The babel implementation seems to call Object.defineProperty in the case where a modification must be made to an existing property descriptor.

It's certainly possible to make TypeScript do the same thing, but this change has significant risk of breaking existing code. This change would likely be observed by many users since TypeScript property declarations are not only used for runtime effect - but also to add type information. (Therefore, it's likely that many are actually using property declarations with no initializer.) See the following example:

class Greeting {
    greeting: string = 'generic greeting';
    greet() { console.log(greeting); }
}
class HelloGreeting extends Greeting {
    greeting: 'hello'; // Narrows type from string to "hello"
}
const greet = new HelloGreeting();
greet.greet();
// Current TS behavior: prints "generic greeting"
// ES proposal behavior: prints undefined

If TS is to stay consistent with ES, then a breaking version change will need to be made (with a flag to opt-in to the existing behavior?)

@joeywatts thanks for raising this -- logged #27644

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blendsdk picture blendsdk  路  3Comments

wmaurer picture wmaurer  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

dlaberge picture dlaberge  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments