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

Keyof doesn't see all apparent members #16578

Closed
olegdunkan opened this issue Jun 16, 2017 · 3 comments
Closed

Keyof doesn't see all apparent members #16578

olegdunkan opened this issue Jun 16, 2017 · 3 comments
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead

Comments

@olegdunkan
Copy link

olegdunkan commented Jun 16, 2017

TypeScript Version: playground
Code

function check<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}
function fn(){}

var f1: Function = fn;
var f2: {():void} = fn;

check(f1, "bind");  //ok
check(f2, "bind"); //error
f2.bind; //ok

Expected behavior:

An index type query keyof T yields the type of permitted property names for T
I expected that "bind" is permitted (as I assume, public and in some cases protected) and apparent members of type {():void}

Form spec:
The apparent members of an object type T are the combination of the following:

  • The declared and/or inherited members of T.
  • The properties of the global interface type 'Object' that aren't hidden by properties with the same name in T.
  • If T has one or more call or construct signatures, the properties of the global interface type 'Function' that aren't hidden by properties with the same name in T.

Actual behavior:
Error.
If it by design I am with great pleasure would like to know what is permitted property names?

@ahejlsberg
Copy link
Member

This is by design. The keyof operator only considers the declared or inherited properties of a type, not those that come from its apparent type.

@ahejlsberg ahejlsberg added the By Design Deprecated - use "Working as Intended" or "Design Limitation" instead label Jun 16, 2017
@olegdunkan
Copy link
Author

olegdunkan commented Jun 16, 2017

@ahejlsberg ok, thanks.

Would you mind if ask a question.
Spec doesn't define a notion of the apparent type, only apparent members .

From spec:
The apparent members of the primitive type String and all string literal types are the apparent members of the global interface type 'String'

I can assume from spec that the global interface String is a apparent type if so then the global interface type 'Function' is also a apparent type.

For global interface String it works, but for the global Function doesn't !!!

check("", "atChar");  //ok

The question is what is the apparent type, what is a difference between String and Function and more interesting what is a motivation to differ (Number, String and Boolean) and (Object, Function) global interfaces in the context of the keyof type query?

@olegdunkan
Copy link
Author

I have made some investigations.
From tsc code follows that the apparent type of type T is {} except:
for string is global interface String,
for number is global interface Number,
for boolean is global interface Boolean,
for type parameter is constraint, if any,
...

function getApparentType(type: Type): Type {
            const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type;
            return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(<IntersectionType>t) :
                t.flags & TypeFlags.StringLike ? globalStringType :
                t.flags & TypeFlags.NumberLike ? globalNumberType :
                t.flags & TypeFlags.BooleanLike ? globalBooleanType :
                t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
                t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
                t;
        }

The motivation to differ (Number, String and Boolean) and (Object, Function) is probably to prevent enumerating of native (intrinsic) properties of Object and Function for user types.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead
Projects
None yet
Development

No branches or pull requests

2 participants