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

Strict null checks, index signatures, and optional properties. #17277

Closed
sccolbert opened this issue Jul 18, 2017 · 4 comments
Closed

Strict null checks, index signatures, and optional properties. #17277

sccolbert opened this issue Jul 18, 2017 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@sccolbert
Copy link

This may be related to #13778

I want to define a type which restricts the allowable values of properties to a specific set of types. Sub-types may define certain properties to be a more specific subset of the allowable types (think JSON object but with well-formed, known keys).

The code below is a simplified demonstration, but I run into problems with sub-types specifying optional properties when strictNullChecks is turned on. Compilation fails with a message saying the property is incompatible with the index signature. It strikes me as odd that this is not allowed, at the same time the index signature does not have an implicit undefined . If anything, the well typed property is more safe than using the index signature. My intuition is not than an index signature should have an implicit undefined, but that the below code should be allowed with strictNullChecks. I realize that foo?: T is treated the same as foo: T | undefined by the compiler, but there is a distinct difference between the lack of presence of key, and a key with undefined as a value. Usually the distinction doesn't matter, but in this case it does.

TypeScript Version: 2.4.0

Code

interface Thing {
  [key: string]: string | number | boolean;
}

interface Other extends Thing {
  bar: boolean;  // always okay
  foo?: string;  // not okay with strictNullChecks
}

Expected behavior:
Compiles with strictNullChecks

Actual behavior:
Does not compile with strictNullChecks

@jcalz
Copy link
Contributor

jcalz commented Jul 19, 2017

I'm sure this is intended behavior.

In the presence of strictNullChecks, the definition of Thing means that it will return a defined value for any string key you use (which I assume would require a Proxy to actually implement). Specifically, it has a defined foo property:

declare let thing: Thing;
thing.foo.toString(); // no possibly-undefined warning 

As you noted, there's not much difference in TypeScript between an optional property and a required property with a possibly-undefined value. If you access an absent property, it will produce an undefined result. So the definition of Other says that accessing the foo property might produce an undefined result, which cannot happen if Other extends Thing.

If you are trying to say that Thing might not have values for every possible key, then you should define it as

interface Thing {
  [key: string]: string | number | boolean | undefined;
}

If your main issue is with the way TypeScript treats missing keys, then maybe you should head over to #13195 and upvote and/or contribute. Cheers!

@sccolbert
Copy link
Author

I guess my main argument is the inconsistency. If we're going to say "okay, index signatures may be undefined if you misspell something, but that would be a pain to force it everywhere even with strictNullChecks", then an optional property should also be compatible with the index signature by the exact same line of reasoning.

@mhegazy mhegazy added the Needs Investigation This issue needs a team member to investigate its status. label Aug 29, 2017
@mhegazy mhegazy added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Needs Investigation This issue needs a team member to investigate its status. labels Nov 9, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Nov 9, 2017

Index signature is a constraint on the type. it is assumed that all properties would fall under this constraint.. otherwise obj[string] would be a lie.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
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

4 participants