Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how come () => void is a subtype of () => a if void isn't a subtype of a and a isn't subtype of void? #8581

Closed
zpdDG4gta8XKpMCd opened this issue May 12, 2016 · 8 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@zpdDG4gta8XKpMCd
Copy link

zpdDG4gta8XKpMCd commented May 12, 2016

declare var a: () => void;
declare var b: () => PromiseLike<void>;

a = b; // <-- why!!!
b = a;


declare var c: void;
declare var d: PromiseLike<void>;

c = d;
d = c;

image

@zpdDG4gta8XKpMCd zpdDG4gta8XKpMCd changed the title how come () => void is a subtype of () => a if void isn't a subtype of a? how come () => void is a subtype of () => a if void isn't a subtype of a and a isn't subtype of void? May 12, 2016
@vladima
Copy link
Contributor

vladima commented May 12, 2016

tl;dr; the assumption is that it is safe to assign something that returns some value to something that returns void so result is ignored, however the opposite is not true.

see 3.11.3 Subtypes and Supertypes

  • S is an object type, an intersection type, an enum type, or the Number, Boolean, or String primitive type, T is an object type, and for each member M in T, one of the following is true:
    • M is a property and S has an apparent property N where
      • M and N have the same name,
      • the type of N is a subtype of that of M,
      • if M is a required property, N is also a required property, and
      • M and N are both public, M and N are both private and originate in the same declaration, M and N are both protected and originate in the same declaration, or M is protected and N is declared in a class derived from the class in which M is declared.
    • M is a non-specialized call or construct signature and S has an apparent call or construct signature N where, when M and N are instantiated using type Any as the type argument for all type parameters declared by M and N (if any),
      • the signatures are of the same kind (call or construct),
      • M has a rest parameter or the number of non-optional parameters in N is less than or equal to the total number of parameters in M,
      • for parameter positions that are present in both signatures, each parameter type in N is a subtype or supertype of the corresponding parameter type in M, and
      • the result type of M is Void, or the result type of N is a subtype of that of M.

@zpdDG4gta8XKpMCd
Copy link
Author

it's not a safe assumption at all, ugh....

@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented May 12, 2016

I have a function that returns a promise: () => Promise<void>, it is passed around as a callback, after refactoring unintentionally it end up being an argument of a parameter of type () => void. Right here I need the compiler to help me and say it can't be used there, because Promise<void> just can't be treated the same way as void, and what does compiler do? nothing, exactly

@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented May 12, 2016

anything that has to be serialized can't be passed round safely because there is a chance it will get swallowed by () => void and then end up being passed to the standard JSON.stringify which will then crash

function sendMessageWhenReadyNoValuesIntended(doMyStuff: () => void) {
    window.postMessage(JSON.stringify({ message: doMyStuff() }));
}
interface RandomCrazyObject {
   self: RandomCrazyObject;
}

function processMyInput() : RandomCrazyObject {
    let result = <RandomCrazyObject>{ };
    result.self = result;
    return result;
}
sendMessageWhenReadyNoValuesIntended(processMyInput())

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label May 12, 2016
@RyanCavanaugh
Copy link
Member

The alternative is much worse -- imagine not being able to write this code:

let arr = [1, 2];
// Error, can't convert 'number' return type to 'void'
[1, 2, 3].forEach(x => arr.push(x));

@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented May 12, 2016

much worse, I agree, you sound more convincing than ever

[1, 2, 3].forEach(x => { arr.push(x); });

@RyanCavanaugh
Copy link
Member

If that's sarcasm I'm missing it 😛

@zpdDG4gta8XKpMCd
Copy link
Author

not much sarcasm in 3 extra characters

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants