Search code examples
typescripttypescript-5

What it the signature for non-static property decorator in TypeScript 5?


Although the TypeScript 5 has been officially realized with new decorators, the decorators documentation is still about decorators of TypeScript 4 (for the 1st April 2023).

enter image description here

The signature of decorator functions has been changed. Experimentally I have 2 parameters with undefined first one and metadata-like second one:

export default function VueReactiveDataField(
  ...blackBoxes: Array<unknown>
): void {

  console.log(blackBoxes);

  // ...

}

@VueComponentOptions
export default class ReactiveStateExperimentalSample {

  @VueReactiveDataField
  protected exampleReactiveField: string = "Experimental sample works fine";

}

The console output is:

enter image description here

What it their types and why first one is undefined?

[ Appendix ] tsconfig.json

Of course is has not "experimentalDecorators": true.

{
  "compilerOptions": {

    "target": "ES2020",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,

    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": false, /* Actual for Vue3; see https://github.com/vuejs/vue-next/issues/4668 */
    "allowJs": true, /* Actual for Vue3; */
    "skipLibCheck": true,

    "baseUrl": "./",
    "paths": {
      "@Source/*": [ "./Source/*" ]
    },
    "declaration": true
  },
  "include": [
    "Project.d.ts",
    "Source/**/*",
    "Tests/**/*"
  ]
}

Solution

  • You can read about decorators in ECMAScript decorators proposal. Specifically for class field decorators the first argument is always undefined. For the second argument TS5 provides ClassFieldDecoratorContext<This, Value> helper type.

    For example

    function VueReactiveDataField(
      _: undefined,
      // `unknown` if you don't care about the type of the class where decorated
      // property belongs
      // `string` so that you can only decorate string properties
      context: ClassFieldDecoratorContext<unknown, string>
    ): void {
      console.log(_, context);
    }
    
    @VueComponentOptions
    export default class ReactiveStateExperimentalSample {
      @VueReactiveDataField
      protected exampleReactiveField: string = "Experimental sample works fine";
    }
    

    Or make it generic

    function VueReactiveDataField<
      This, 
      // Will only work for properties of type `string`, `number` or `string | number` (or `never` but that's boring)
      Value extends string | number
    >(
      _: undefined,
      context: ClassFieldDecoratorContext<This, Value>
    ): void {
      console.log(_, context);
    }
    
    @VueComponentOptions
    export default class ReactiveStateExperimentalSample {
      @VueReactiveDataField
      protected exampleReactiveField: string = "Experimental sample works fine";
    }