TypeScript Version: 3.7.2 & 3.8.0-dev.20191128
Search Terms:
destructuring default value function
Code
interface ReturnVal {
something(a: string): string // has one param
}
function getFn(): ReturnVal {
return {
something(a) { return a }
}
}
interface PartialOptions {
something?(a: string, b?: string): string // has two params
}
function run(options: PartialOptions) {
const { something = getFn().something } = options;
const something2 = options.something ?? getFn().something;
return {
method() {
something('', '') // this should not error
something2('', '') // also wrong?
// and now the twist: hovering "something" and something2 (above) shows *correct* signatures. :)
}
}
}
Expected behavior:
Pick up the function signature of PartialOptions when checking the call.
Actual behavior:
Hover shows correct signature. Type check breaks saying single parameter is expected.
Playground Link:
http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=29&pc=1#code/JYOwLgpgTgZghgYwgAgEoTAVyiAanAG2QG8AoZC5AZwHsBbDAC1AHMAKOALmrClYEpuVXq2QB6MckZwqyGiBQAHOFDh1SAX1KkYmEAjDB5yFhgBiINoLQZseQiXKUotnI8ofq9Jqw78SyC5YbnDIWh5aWqSgkLCIKAAKKoaEAPKKhvKyZB60DGDMICwA-BxCIkUANMgARsXlfEXWwo0s4pLSsmAA7jTIyqp0VJrauvqZIIF6bDQZRiBU3ElQKQTpE1T+OZQIWWABeT5FyAC8JuaW-AB0hwWiGqdyc1kA3Noeuwv7t4UsAEyPWYbG7eO7HYrFc5gCxWEH5X5vJwUIJ2dyeCjwmgAEysaPRuVBvzYAHJidVSf4JMg7rIqIwaJgCFjkCAaPtoFAaFAkfiKD9WH8SWTkBT2shCLRkN1OUViu9eZQqXAQMzWd1qYwUD1gMJuPSAG7QUQAIn5RWN4pVXnhAuQHBqNEN-jpNG6sgAVLsoC4DO7qMAWCA4MEIFQrshOPweREeZFSEA
Related Issues:
found many issues about destructuring, but couldn't identify an issue about default values for functions. some are about generics behaviour, etc.
This is certainly the most interesting bug of the day. A reduced form:
interface ReturnVal {
something(): void;
// Workaround line:
// something: () => void;
}
// Comment this out to remove the error
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}
Our hypothesis is that we're not using the method bivariance as part of the cache key for relationships and we end up over-reducing the something initializer to the zero-argument form.
Fun one. Issue one is that types () => void and (x?: string) => void are both subtypes of each other. Issue two is that we sort union type constituents by their type ID before we perform subtype reduction. Type IDs depend on the order in which type relation checks are performed (or not performed), which is why commenting out the seemingly unrelated const declaration causes downstream effects.
Possible remedies include making the subtype relationship more selective or better preserving the order of constituent types for operations like || and && before performing subtype reduction.
Most helpful comment
Fun one. Issue one is that types
() => voidand(x?: string) => voidare both subtypes of each other. Issue two is that we sort union type constituents by their type ID before we perform subtype reduction. Type IDs depend on the order in which type relation checks are performed (or not performed), which is why commenting out the seemingly unrelatedconstdeclaration causes downstream effects.Possible remedies include making the subtype relationship more selective or better preserving the order of constituent types for operations like
||and&&before performing subtype reduction.