-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Using the "in" operator should make bracket access safe on an object #34867
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
Comments
And just realized 'keyof object' is of type never. So you can't pass anything reasonable to the functions that do compile :) |
Possible duplicate of #21732 |
This is a bit tricky; we'd need a new kind of type property key that relates to a (hopefully const?) variable, or some special-casing which isn't great. These kind of helper functions are usually short enough and general-purpose enough that having a type assertion or two in the body doesn't change their safety very much, so the trade-off isn't great in terms of complexity. Hearing about more common / safety-requiring scenarios would be good. |
The property accessor can only use string or symbols which are both already immutable in javascript, right? |
The value itself is immutable, but the references to them aren't (unless they're bare |
right, and object isn't immutable either. You'd need something stronger than types to say "This key works for this specific object as long as no else has edited the object or the reference." The const thing is interesting. I figured your type checks already have something that's smart about reassignments i.e. in the playground this doesn't compile cause it's smart enough to realize that value might be undefined.
|
See #31445 - the compiler would need a way to know that the same |
The same issue occurs with the This seemingly simple example fails compilation at function processUnknown(obj: unknown) {
if (typeof obj === 'object') {
if (obj !== null) {
for (const key in obj) {
// ^^^
// Typescript identifies key as type `string`
console.log(`key ${key} indexes ${obj[key]}`);
// ^^^^^^^^
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
// No index signature with a parameter of type 'string' was found on type '{}'.(7053)
}
}
}
} However following runtime examples show that this access is safe, regardless of property type. (The only thing I can see breaking this is if somehow the property is removed between processUnknown(JSON.parse('{"someJsonKey":"someJsonValue","someOtherJsonKey":1}'))
// key someJsonKey indexes someJsonValue
// key someOtherJsonKey indexes 1
processUnknown({ "1": "one (string)", 2: "two (number)" })
// key 1 indexes one (string)
// key 2 indexes two (number) Playground Link for above examples Similar to the plain Processing parsed JSON shape is the use case I came across this with as well. The example below may motivate things a bit more: function processParsedJson(id: string, jsonObj: unknown) {
if (typeof jsonObj === 'object') {
if (jsonObj !== null) {
let keysExist = false
for (const key in jsonObj) {
keysExist = true
console.log(`parsed JSON ${id} has key ${key} pointing to ${jsonObj[key]}`);
}
if (!keysExist) {
console.log(`parsed JSON ${id} did not have any keys`)
}
}
else {
console.log(`parsed JSON ${id} was null object type`)
}
}
else {
console.log(`parsed JSON ${id} was non-object type ${typeof jsonObj}`)
}
}
processParsedJson('1', JSON.parse('{"someKey":"someValue","someOtherKey":1}'))
// parsed JSON 1 has key someKey pointing to someValue
// parsed JSON 1 has key someOtherKey pointing to 1
processParsedJson('2', JSON.parse('{}'))
// parsed JSON 2 did not have any keys
processParsedJson('3', JSON.parse('2'))
// parsed JSON 3 was non-object type number
processParsedJson('4', JSON.parse('["a", 2, null, {}]'))
// parsed JSON 4 has key 0 pointing to a
// parsed JSON 4 has key 1 pointing to 2
// parsed JSON 4 has key 2 pointing to null
// parsed JSON 4 has key 3 pointing to [object Object]
processParsedJson('5', JSON.parse('null'))
// parsed JSON 5 was null object type (Note: JSON.parse currently returns type |
I had the same issue today (Playground Link) I was able to get around it by defining an existsIn helper as follows:
And then I was able to define the function I wanted
maybe there's some unsoundness I haven't considered here, but it seems like it should be pretty simple to have |
@RyanCavanaugh What is the typescript recommended way to determine that an unknown variable is an object with a given property on it? @nightpool's suggestion is fairly good but like he said, "it should be pretty simple to have const obj: unknown = { foo: 'bar'}
if (typeof obj === 'object' && obj !== null && Object.hasOwnProperty.call(obj, 'foo') && typeof obj.foo === 'string') {
// ^^^^^ ERROR
} A common use case is when catching an error. The error must either be typed as unknown or any but it's a hassle to type check an error that has additional properties on it. For example, node errors usually have a There needs to be a documented, typescript-recommended way of accomplishing this. |
Search Terms
object in keyof bracket operator in
Suggestion
Checking a key with the "in" operator on an object should make it safe to to use in brackets
Use Cases
Useful when writing functions that process input JSON/doesn't have a concrete type.
Examples
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: