You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In ECMAScript 5 environments, Object.keys throws an exception if passed any non-object argument:
// Throws if run in an ES5 runtimeObject.keys(10);
In ECMAScript 6, Object.keys returns [] if its argument is a primitive:
// [] in ES6 runtimeObject.keys(10);
Rationale and Change
This is a potential source of error that wasn't previously identified.
In TypeScript 3.5, if target (or equivalently lib) is ES5, calls to Object.keys must pass a valid object.
Workarounds
In general, errors here represent possible exceptions in your application and should be treated as such.
If you happen to know through other means that a value is an object, a type assertion is appropriate:
This checking is very specific to certain patterns because structural subtyping is common:
functionprintPoint(pt: {x: number,y: number}){console.log(pt.x,', ',pt.y);}constOrigin={x: 0,y: 0,name: "origin"};// OK - 'name' is not an error hereprintPoint(Origin);
Rationale and Change
In TypeScript 3.4 and earlier, certain excess properties were allowed in situations where they really shouldn't have been.
Every instance of this has so far represented a legitimate bug in the codebase it was found in.
We don't have any recommended workarounds other than to simply fix those bugs.
Zero-Candidate Generic Inference produces unknown instead of {}
Background
During a generic function call, TypeScript relates the function arguments to its parameters to determine the type parameters' types:
typeBox<T>={contents: T};functionbox<T>(arg: T): Box<T>{return{contents: arg;}}// box's 'T' parameter inferred to be 'string'constp=box("hello");
Each possible type for a type parameter is called a candidate, and TypeScript selects the "best" candidate according to a set of criteria.
However, sometimes there are no candidates:
In prior versions of TypeScript, T would be {}, or the constraint type if one was present.
Rationale and Change
At the time generics were added to the language, {} was the most accurate choice available, but unknown is a safer default.
In TypeScript 3.5, when inference finds no candidates for a type parameter, the type unknown will be used instead if there is no constraint type.
unknown is a much more accurate representation of what will happen at runtime.
Critically, if a generic function f<T> is not given a value of type T, it has no ability to produce a value of type T, and the caller should not assume anything about values in the return position typed as T.
Prevalence
Having zero inference candidates in a function call should be rare.
The most common place where this does occur is in code that uses generics to appear more typesafe than it is:
functionparse<T>(x: string): T{returnJSON.parse(x);}// Generic call, must be safe??constk=parse<string[]>("[0]");
This pattern is misleading and should be avoided!
Because an unsafe operation is occurring, it's more appropriate to use a type assertion:
functionparse(x: string): unknown{returnJSON.parse(x);}// Unsafety is more obviousconstk=parse("[0]")asstring[];
When these functions are called without type parameters, unknown is now produced automatically:
{ [k: string]: unknown } no longer a wildcard assignment target
Background
The index signature { [s: string]: any } in TypeScript behaves specially: It is a valid assignment target for any object type.
This is a special rule - the definition of index signatures would not normally produce this behavior.
Since its introduction, the type unknown in an index signature behaved the same way:
letdict: {[s: string]: unknown};// Was OKdict=()=>{};
In general this rule makes sense; the implied constraint of "All its properties are some subtype of unknown" is trivially true of any object type.
Rationale and Change
In TypeScript 3.5, this special rule is removed for { [s: string]: unknown }.
This was a necessary change because of the change from {} to unknown when generic inference has no candidates.
Consider this code:
Some codebases have adopted { [s: string]: unknown } to mean "an arbitrary object".
Depending on the intended behavior, several alternatives are available:
object
{ [s: string]: any }
{ [s: string]: {} }
We recommend sketching out your desired use cases and seeing which one is the best option for your particular use case.
In TypeScript 3.5, the logic used to validate a write was much too permissive:
functionwrite<KextendskeyofA>(arg: A,key: K,value: A[K]): void{// ???arg[key]="hello, world";}// Breaks the object by putting a string where a number should bewrite(a,"n");
In TypeScript 3.6, this logic is fixed and the above sample correctly issues an error.
Workarounds
Most instances of this error represent potential errors in the relevant code.
One example we found looked like this:
typeT={a: string,x: number,y: number};functionwrite<KextendskeyofT>(obj: T,k: K){// Trouble waitingobj[k]=1;}constsomeObj: T={a: "",x: 0,y: 0};// Note: write(someObj, "a") never occurs, so the code is technically bug-free (?)write(someObj,"x");write(someObj,"y");
This function can be fixed to only accept keys which map to numeric properties:
// Generic helper type that produces the keys of an object// type which map to properties of some other specific typetypeKeysOfType<TObj,TProp,KextendskeyofTObj=keyofTObj>=KextendsK ? TObj[K]extendsTProp ? K : never : never;functionwrite(obj: SomeObj,k: KeysOfType<SomeObj,number>){// OKobj[k]=1;}constsomeObj: SomeObj={a: "",x: 0,y: 0};write(someObj,"x");write(someObj,"y");// Correctly an errorwrite(someObj,"a");