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

A custom type guard produces unexpected behavior inside a generic function #40990

Closed
mckravchyk opened this issue Oct 8, 2020 · 3 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@mckravchyk
Copy link

TypeScript Version: 4.1.0-insiders.20201007

Search Terms:
type guard generics

Code

interface IndexedObject {
    [key: string]: any
}

/**
 * Check if the value is a "plain" JavaScript object initialized with { } (not instance of any class)
 * 
 * In addition to the runtime check, this function should assert that the 
 * argument matches IndexedObject interface
 * 
 */
const isPlainObject = (value: any): value is IndexedObject => (
    typeof value === 'object'
    && value !== null
    && value.constructor === Object
    && Object.getPrototypeOf(value) === Object.prototype
);

function genericFunction<T>(value: T): T {
    if (isPlainObject(value)) {
        type x = typeof value; // T - not quite right
        return value;
    }

    type x = typeof value; // "never"

    return value;
}

function normalFunction(value: any): void {
    if (isPlainObject(value)) {
        type x = typeof value; // IndexedObject - ok
    }

    type x = typeof value; // any - ok
}

Expected behavior:
Inside genericFunction() in the if block, the type of value should be T & IndexedObject. After the if block, the type of value should be x

Actual behavior:
Inside the genericFunction(), in the if block, type of value is T, and after the if block - type of value is never. It works ok in a non-generic function

Playground Link: https://www.typescriptlang.org/play?ts=4.1.0-insiders.20201007#code/JYOwLgpgTgZghgYwgAgJIgCYQB4QweQCMArCBMZAbwChk7kBtAawgE8AuZAZzClAHMAupzghW1AL7VqAegBUc2nOQBhABZkmyYDGRgNyAG5wANgFcUwLsjjIARAAcTcUHeQApOMYDKCPg4oAexIyClBgMGBTYAAvPGQAdwi1KmQJZAAKEECwkB5RJGRA3VFWZARnLi4ASiVkOvQbDAwI4ECQPUC9AygzcGAAWxQEDQQmABpuq2QYPvI2jq41QLMTDBsq6Ap9OG2DOrgofjMh8GQB3ZGIa3QsXAIQ8m1waHgkOqUZagR2nm0uAAKzlARFITwAvJljOYICIxNVONCLP80JgcHhQaFkOCAHyZWj0MCsBwQYpGUzI8FU5AAcmCYLANIJdAAZCzyTDkABCakgVYmZnINkciwAOh+eV4ZnIgSg2OpmPIguFirAov4EDAAKgORyxIg+BgGSREGq8shqtFDh1YD1JOo1QA3NJZiB5u1kBqQNBgAgAGJzSLtAA8ABUccaKbDkKGETGqIKdJkrECXCBVZGYdUzTR6Hm9PrkNhsQWSWSTY7kDIZPGALTIbIUACOZgiKD4-DUYEFeagmrMUA6FcFUkFRJJRZL49JugrVZrdm9hmgdmkvf7g5FEGdo9d7o62SgFxMAbdQZAmYscNYccMgWA61z9CTGRTwPTjzAl9NOZ7hMLxaQtO5ZRpW1aoncGKfsg9aBEwI5rv+E6AaWM5bmBNalDBRTwRIQA

Comment
I am aware that this is not an ideal use case. "plain object" as I defined it, is not strictly tied to IndexedObject interface - which might cause unexpected problems with types. I have decided to not use a type guard for this function, nevertheless - I wanted to submit this as it seems to be a bug. I could not reproduce a more "generic" example.

@andrewbranch
Copy link
Member

Duplicate of #4742

@andrewbranch andrewbranch marked this as a duplicate of #4742 Oct 8, 2020
@andrewbranch andrewbranch added the Duplicate An existing issue was already created label Oct 8, 2020
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@mckravchyk
Copy link
Author

I have just tested it in 5.0.2 an it works correctly now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants