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

Add generic type parameters #102

Closed
joelpurra opened this issue Jan 22, 2020 · 2 comments
Closed

Add generic type parameters #102

joelpurra opened this issue Jan 22, 2020 · 2 comments

Comments

@joelpurra
Copy link
Contributor

What do you think about adding generic type parameters to some of the is methods too?

Originally posted by @sindresorhus in #97

@sindresorhus sindresorhus changed the title Adding generic type parameters Add generic type parameters Jan 22, 2020
@joelpurra
Copy link
Contributor Author

joelpurra commented Jan 22, 2020

Would be nice to propagate some more generic typings for the cases when typescript already knows about them, either inferred/implicitly or provided/explicitly.

Note that is (and now the associated assertions) "actively" perform type checking work at runtime. The current implicit default is that generic types resolve to unknown1. This is a sane default, which is meant to force the user to perform further type checks before using the value. Allowing developers to define a generic type (inferred or explicit) at compile-time can hide the need to perform runtime type checks, leading to unexpected behavior. It is therefore up to developer to be aware and use explicit generics responsibly.

1 Apart from a few assertion methods where I jumped the gun in #97.

Examples

(Using a patched is with propagated generic types for both is and assertions.)

Works as expected when not providing an explicit T.

(async function () {
    const t = Promise.resolve("an unexpected string value");
    // t is inferred at compile-time to be of type Promise<string>
    assert.promise(t);
    // t is (still) inferred at compile-time to be of type Promise<string>
    // t is known at runtime to be a Promise resolving to an unknown type

    const r = await t;
    // r is inferred at compile-time to be of type string
    console.log(r * 2);
    // does not compile as r is not a number
}());

Showing intersection type mixup leading to unexpected behavior.

(async function () {
    const t = Promise.resolve("an unexpected string value");
    // t is inferred at compile-time to be of type Promise<string>
    assert.promise<number>(t);
    // t is inferred at compile-time to be of intersection type Promise<string> & Promise<number>

    const r = await t;
    // r is assumed to be of type never, which is the intersection between string and number
    console.log(r * 2);
    // still (unexpectedly?) compiles, but logs NaN
}());

Showing that generic types are not checked at runtime.

(async function () {
    const t: unknown = Promise.resolve("an unexpected string value");
    // t is inferred at compile-time to be of type unknown
    assert.promise<number>(t);
    // t is known at runtime to be a Promise, but is incorrectly assumed to be Promise<number> resolving to a number

    const r = await t;
    // r is assumed to be of type number
    console.log(r * 2);
    // logs NaN
}());

(Sidenote: if the resolved string would have been implicitly castable by the javascript runtime to a number, such as "1234", the multiplication would actually work at runtime.)

Allowing developers to provide their own generic types would be a nice improvement, but guess it needs a disclaimer about potential compile-time/runtime discrepancies. If it's considered too risky, so to speak, I'd be happy to remove the generics introduced in #97.

@sindresorhus
Copy link
Owner

Fixed by #103

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants