Problem
The above error "Type 'unknown' is not assignable to type 'x'" occurs when we assign a value of type "unknown" to a variable of a different type. The type "unknown" is a type-safe alternate of the type "any". Therefore, if TypeScript doesn't know an object's type, it implicitly assigns it with the type "unknown".
Solutions
We can address this problem in different ways depending upon how type-safe you want it to be. I'll discuss two common solutions to this problem, namely, Type Assertion and Type Guard.
In the above-mentioned solutions, Type Assertion is more of a quick fix and should be used with caution. Type Guard is a more dominant type-safe method of handling the issue. Let's look at the breakdown of both solutions.
Type Assertion
In the following example, I have explicitly mentioned the type of a value as "unknown" for the sake of understanding.
const a: unknown = "Sharooq";
const b: string = a;
In real-world scenarios, the "unknown" type could normally occurs when we are fetching data from an API or on accessing state values passed thorugh routing and other such scenarios where TypeScript doesn't know the actual type of the value.
The above code resulted in the error "Type 'unknown' is not assignable to type 'string'.
In type assertion, you can specify to TypeScript that a value with the type "unknown" is actually of some other type. The following code does exactly that.
const a: unknown = "Sharooq";
const b: string = a as string;
Here, we ensure TypeScript that we are 100% confident that the value of a
is of type "string" using the as
operator.
You should only use Type Assertion if you are confident about the type of the value or the object that we are passing. Wrong use of Type Assetion could cause more trouble than good.
Type Guard
Type guarding is where we implicitly check the type of an unknown value or an object before assigning it to another variable. See the following example.
const value1: unknown = "Sharooq";
let value2 = "";
if (typeof value1 === "string") {
value2 = value1;
}
console.log("value2", value2)
In this case, we explicitly check the actual type of the value with the "unknown" type.
In the above example, I have explicitly given the type "unknown" to the varaible "value1" simply for imitating the scenario.
If you run the above code, it doesn't throw any error. This is because TypeScript understands that the actual type of "value1" is a "string" from the if
statement and hence proceeds to assign it to the variable "value2".
The typeof
operator returns the actual type of a value.
Advanced use of Type Guard technique.
In real use cases, we might be dealing with more complex objects and hence would require a more elaborate type checking approach to ensure that the value matches a certain type
. See the following example.
type Profile = {
firstName: string;
lastName: string;
};
function isUser(obj: any): obj is Profile {
return typeof obj === "object" && obj !== null && "name" in obj && "country" in obj;
}
const user: unknown = {
firstName: "Sharooq",
lastName: "Salaudeen",
};
let sharooq: Profile;
if (isUser(user)) {
// 👉️ person has type of Person here
sharooq = user;
} else {
sharooq = { firstName: "", lastName: "" };
}
console.log(sharooq);
Let me explain what is happening in the above code.
First, we started by defining the "Profile" type.
Second, we created a function "isUser", which takes an input object and returns the value true
if it meets the conditions mentioned inside the function. Here, we have written a detailed condition check to ensure that the object is of the type "Profile".
In the above "isUser" function, we are checking the input parameter "obj" is not of a null value. This is because, in JavaScript and TypeScript, null
is treated as an "object". Therefore, it could break the type checking if unhandled. Welcome to the weird world of JavaScript. ;D
Next, we created a variable "user" with sample data and explicitly assigned the type "unknown" to it. Then, we created a mutable variable "sharooq" using the let
keyword.
By using the "Type Guard" technique, we ensured that the "user" variable is of the type "Profile" and it contains the properties "firstName" and "lastName". Then we assigned the corresponding value to the variable "sharooq". This makes TypeScript understand that the value of the "unknown" type is in fact of the type "Profile" and can be assigned to any variable with the corresponding type without raising a type error.