google / closure-compiler

A JavaScript checker and optimizer.
https://developers.google.com/closure/compiler/
Apache License 2.0
7.4k stars 1.15k forks source link

Recursive record types fail silently, documentation lacks #4031

Open fingerartur opened 1 year ago

fingerartur commented 1 year ago

Recursive record types are seemingly not supported. However, it is not directly documented (that I know of). The behavior of the compiler is that it assigns unknown type to the recursive part.

/**
 * @typedef {{ name: string, next: Car }} // should produce error, but instead it treats `.next` as `?` (unknown type)
 */
var Car;

/**
 * @type {Car}
 */
const car = {
  name: 'BMW',
  next: {
    name: 'Audi',
  }
}

In this example the .next attribute becomes unknown. This is not ideal, it is by no means obvious that recursion is not supported. The compiler should raise an error/warning saying that recursion in record types is not supported.

It would also be useful to add a section in the wiki about the correct syntax for recursive types. There is a mention in #2011 that they can be implemented using the @record keyword. It would be helpful to add this to wiki as well.

Compiler Version: v20221102

Build command:

java -jar ./scripts/closureCompiler.jar \
  --entry_point=./src/js/index.js \
  --js=./src/**.js \
  --dependency_mode=PRUNE \
  --warning_level=VERBOSE \
  --js_output_file=./dist/bundle.js \
  --module_resolution=WEBPACK \
  --compilation_level=ADVANCED \
  --jscomp_error=checkDebuggerStatement \
  --jscomp_error=unusedLocalVariables \
  --jscomp_error=reportUnknownTypes \
  --jscomp_error=strictCheckTypes;
rishipal commented 1 year ago

More than record types, I think the problem is that typedef fails silently. They aren't supposed to be recursive, as they're used define an alias for an existing type. So we should probably warn.

/**
 * @typedef {{ name: string, next: Car }}  
 */
var Car;

The @type using recursive types does report:

/**
 * @type {{ name: string, next: Car }}  ----> WARNING - [JSC_UNRECOGNIZED_TYPE_ERROR]
 */
var Car;