closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

`@type` doesn't work on functions when a forward reference or requireType'd

Open wallw-teal opened this issue 4 years ago • 5 comments

Inline Case (working)

This has the typedef inline in a single file:

/**
 * @typedef {function(!string, number):string}
 */
let Foo;

/**
 * @type {Foo}
 */
const impl = (foo, bar) => [foo]; // incorrectly returns an array
const result = impl('test', 1);
impl(result, 2);

Compiler Args:

java -jar node_modules/google-closure-compiler-java/compiler.jar --dependency_mode=PRUNE --language_in=ECMASCRIPT_2018 --warning_level=VERBOSE --jscomp_error=accessControls --jscomp_error=checkPrototypalTypes --jscomp_error=checkRegExp --jscomp_error=checkTypes --jscomp_error=checkVars --jscomp_error=conformanceViolations --jscomp_error=const --jscomp_error=constantProperty --jscomp_error=deprecatedAnnotations --jscomp_error=duplicateMessage --jscomp_error=es5Strict --jscomp_error=externsValidation --jscomp_error=functionParams --jscomp_error=globalThis --jscomp_error=invalidCasts --jscomp_error=misplacedTypeAnnotation --jscomp_error=missingGetCssName --jscomp_error=missingOverride --jscomp_error=missingPolyfill --jscomp_error=missingProperties --jscomp_error=missingProvide --jscomp_error=missingRequire --jscomp_error=missingReturn --jscomp_error=missingSourcesWarnings --jscomp_error=moduleLoad --jscomp_error=msgDescriptions --jscomp_error=nonStandardJsDocs --jscomp_error=partialAlias --jscomp_error=polymer --jscomp_error=strictModuleDepCheck --jscomp_error=strictPrimitiveOperators --jscomp_error=suspiciousCode --jscomp_error=typeInvalidation --jscomp_error=undefinedNames --jscomp_error=undefinedVars --jscomp_error=underscore --jscomp_error=unknownDefines --jscomp_error=unusedLocalVariables --jscomp_error=unusedPrivateMembers --jscomp_error=useOfGoogBase --jscomp_error=uselessCode --jscomp_error=untranspilableFeatures --jscomp_error=visibility --jscomp_warning=deprecated --jscomp_off=reportUnknownTypes --jscomp_off=strictCheckTypes --jscomp_off=strictMissingProperties --compilation_level=ADVANCED --js_output_file=output.js --js input.js --entry_point input.js

Result:

input.js:9: ERROR - [JSC_TYPE_MISMATCH] inconsistent return type
found   : Array<?>
required: string
const impl = (foo, bar) => [foo]; // incorrectly returns an array
                           ^^^^^

1 error(s), 0 warning(s), 100.0% typed

That's all well and good as expected.

Goog Module Case (broken)

Using goog.module results in the error not occurring:

// typedef.js
goog.module('Foo');

/**
 * @typedef {function(!string, number):string}
 */
let Foo;

exports = Foo;
// input.js
goog.module('test');

const Foo = goog.requireType('Foo');

/**
 * @type {Foo}
 */
const impl = (foo, bar) => [foo]; // incorrectly returns an array

const result = impl('test', 1);
impl(result, 2);

Compiler Args:

java -jar node_modules/google-closure-compiler-java/compiler.jar --dependency_mode=PRUNE --language_in=ECMASCRIPT_2018 --warning_level=VERBOSE --jscomp_error=accessControls --jscomp_error=checkPrototypalTypes --jscomp_error=checkRegExp --jscomp_error=checkTypes --jscomp_error=checkVars --jscomp_error=conformanceViolations --jscomp_error=const --jscomp_error=constantProperty --jscomp_error=deprecatedAnnotations --jscomp_error=duplicateMessage --jscomp_error=es5Strict --jscomp_error=externsValidation --jscomp_error=functionParams --jscomp_error=globalThis --jscomp_error=invalidCasts --jscomp_error=misplacedTypeAnnotation --jscomp_error=missingGetCssName --jscomp_error=missingOverride --jscomp_error=missingPolyfill --jscomp_error=missingProperties --jscomp_error=missingProvide --jscomp_error=missingRequire --jscomp_error=missingReturn --jscomp_error=missingSourcesWarnings --jscomp_error=moduleLoad --jscomp_error=msgDescriptions --jscomp_error=nonStandardJsDocs --jscomp_error=partialAlias --jscomp_error=polymer --jscomp_error=strictModuleDepCheck --jscomp_error=strictPrimitiveOperators --jscomp_error=suspiciousCode --jscomp_error=typeInvalidation --jscomp_error=undefinedNames --jscomp_error=undefinedVars --jscomp_error=underscore --jscomp_error=unknownDefines --jscomp_error=unusedLocalVariables --jscomp_error=unusedPrivateMembers --jscomp_error=useOfGoogBase --jscomp_error=uselessCode --jscomp_error=untranspilableFeatures --jscomp_error=visibility --jscomp_warning=deprecated --jscomp_off=reportUnknownTypes --jscomp_off=strictCheckTypes --jscomp_off=strictMissingProperties --compilation_level=ADVANCED --js_output_file=output.js --js input.js --js typedef.js '--js=!node_modules/google-closure-library/closure/goog/**test.js' '--js=!node_modules/google-closure-library/third_party/**test.js' '--js=node_modules/google-closure-library/closure/goog/**.js' '--js=node_modules/google-closure-library/third_party/**.js' --hide_warnings_for=/google-closure-library/ --entry_point input.js

Result: No errors. Expected result: Same error as above

Goog Provide Case (different result)

// typedef.js
goog.provide('Foo');

/**
 * @typedef {function(!string, number):string}
 */
Foo;
// input.js
goog.require('Foo');

/**
 * @type {!Foo}
 */
const impl = (foo, bar) => [foo]; // incorrectly returns an array

const result = impl('test', 1);
impl(result, 2);

Compiler Args:

java -jar node_modules/google-closure-compiler-java/compiler.jar --dependency_mode=PRUNE --language_in=ECMASCRIPT_2018 --warning_level=VERBOSE --jscomp_error=accessControls --jscomp_error=checkPrototypalTypes --jscomp_error=checkRegExp --jscomp_error=checkTypes --jscomp_error=checkVars --jscomp_error=conformanceViolations --jscomp_error=const --jscomp_error=constantProperty --jscomp_error=deprecatedAnnotations --jscomp_error=duplicateMessage --jscomp_error=es5Strict --jscomp_error=externsValidation --jscomp_error=functionParams --jscomp_error=globalThis --jscomp_error=invalidCasts --jscomp_error=misplacedTypeAnnotation --jscomp_error=missingGetCssName --jscomp_error=missingOverride --jscomp_error=missingPolyfill --jscomp_error=missingProperties --jscomp_error=missingProvide --jscomp_error=missingRequire --jscomp_error=missingReturn --jscomp_error=missingSourcesWarnings --jscomp_error=moduleLoad --jscomp_error=msgDescriptions --jscomp_error=nonStandardJsDocs --jscomp_error=partialAlias --jscomp_error=polymer --jscomp_error=strictModuleDepCheck --jscomp_error=strictPrimitiveOperators --jscomp_error=suspiciousCode --jscomp_error=typeInvalidation --jscomp_error=undefinedNames --jscomp_error=undefinedVars --jscomp_error=underscore --jscomp_error=unknownDefines --jscomp_error=unusedLocalVariables --jscomp_error=unusedPrivateMembers --jscomp_error=useOfGoogBase --jscomp_error=uselessCode --jscomp_error=untranspilableFeatures --jscomp_error=visibility --jscomp_warning=deprecated --jscomp_off=reportUnknownTypes --jscomp_off=strictCheckTypes --jscomp_off=strictMissingProperties --compilation_level=ADVANCED --js_output_file=output.js --js input.js --js typedef.js '--js=!node_modules/google-closure-library/closure/goog/**test.js' '--js=!node_modules/google-closure-library/third_party/**test.js' '--js=node_modules/google-closure-library/closure/goog/**.js' '--js=node_modules/google-closure-library/third_party/**.js' --hide_warnings_for=/google-closure-library/ --entry_point input.js

Result:

input.js:6: ERROR - [JSC_TYPE_MISMATCH] initializing variable
found   : function(?, ?): ?
required: NoResolvedType
const impl = (foo, bar) => [foo]; // incorrectly returns an array
             ^^^^^^^^^^^^^^^^^^^

Expected result: Same error as inline case

I also noticed that if I move the @type annotation to the function expression that the compiler errors indicating that @type is not supported on functions. Is the only solution to this to copy/paste the full function definition everywhere or is there a better alternative?

Also, this may be related to #3041 but I was unsure.

compiler version: 20200204.0.0 library version: 20200204.0.0

wallw-teal avatar Feb 13 '20 22:02 wallw-teal