This issue was imported from Closure Compiler's previous home at http://closure-compiler.googlecode.com
The link to the original issue is:
http://blickly.github.io/closure-compiler-issues/#952
Optional field type is inconsistent between @param and @type.
I think the behavior of @param is correct.
Example:
/**
* @typedef {{foo: (string|undefined)}}
*/
var Foo;
/**
* @param {Foo} obj
*/
function bar(obj) {}
bar({}); // ok
/** @type {Foo} */
var f = {}; // warning!
JSC_TYPE_MISMATCH: initializing variable
found : {}
required: {foo: (string|undefined)} at line 14 character 8
var f = {}; // warning
^
The warning looks fine to me. When you declare f you define its type via annotation but then you assign an object of incompatible type, and this is what triggers the warning. Try the following:
/** @type {Foo} */
var f = /** @type {Foo} */ ({}); // Works fine
Btw, in most cases you may skip first @type since in this case type of the variable can be deducted from the assignment.
@ihsoft If the warning is correct, the above @param and bar({}) should be error.
and we need another type definition for optional field.
TypeScript has the optional type:
var f: {foo?: string} = {};
There are two kinds of record types inferred and declared. Declared record types are invariant and "optional" fields must be explicitly set to undefined, inferred record types are more lenient. This is general source of confusion.
bar({}); // inferred.
/** @type {Foo} */ var f = {}; // declared
A agree with @teppeis' comments in the thread and would favor a simple solution that adds support for optional properties for @typedef types.
If the compiler's current behavior is intended because of a distinction between inferred vs. declared record types, I don't see the reasoning behind that. I like the syntax suggested in the comment above
/** @typedef {{foo?: string}} */
Bikeshedding, but foo=: string would be consistent with optional parameters.
Perhaps even foo: string=.
Declared record types aren't necessarily helpful:
/** @typedef {{foo: (string|undefined), bar: number}} */
var Foo;
/** @param {Foo} arg */
function f(arg) {}
f(/** @type {Foo} */ ({ bar: 'String', nonexistent: 42 })); // OK
I'd prefer to explicitly write out foo: undefined to appease the compiler than throw away type checking altogether.
This should now work.
@SLaks I think the behavior of code like:
/**
* @typedef {{foo: (string|undefined)}}
*/
var Foo;
is correct, but we still lack a short syntax for optional fields.
Most helpful comment
@SLaks I think the behavior of code like:
is correct, but we still lack a short syntax for optional fields.