World of Any and Unknown in Typescript
TypeScript's any vs. unknown: Discover how any offers flexibility at the cost of type safety, while unknown enforces stricter checks, ensuring safer code practices.
Apart from the primitive types i.e. string, number, boolean ,null and undefined, there are two special types in TypeScript :
Most of us are familiar with any type because that is the first thing we commonly use when we could not find a type or we feel lazy to write the type for a big and complex object.
For those who are seeing this first time. Here is a short brief on any:
Any
If you want to avoid type checking and you do not want typescript to complain about it you can simply use any type. As the name suggests, any accepts all the types. It is also known as top type
The top type is sometimes called also universal type, or universal supertype as all other types in the type system of interest are subtypes of it.
Syntax
Implications of assigning any type
- You can assign anything to
any typed variable.
let a: any;
a = function () {};
a = false;
a = 1;
a = "Universe";
a = [1, 2, 3, 4];
a = null;
- You can also assign
any typed variable to other type of variables.
let a: any = { name: "Universe" };
const b: string = a;
const c: boolean = a;
const d: number = a;
- You can assess the properties which do not exist on
any typed variable.
let a: any = 1;
/**
* typescript will not complain as type
* checking is ignored for any type.
* There will a runtime error:
* a.getGalaxyName is not a function
*
* Value of b will be undefined
*/
a.getGalaxyName();
const b = a.name;
- You can call
any typed variable as function, even if it is not a function.
let a: any = 1;
/**
* typescript will not complain as
* type checking is ignored for any type
* There will be a runtime error:
* a is not a function
*/
a();
Word of wisdom
Use any type as last resort because it takes away all the power that is provided by typescript
Enter the world of unknown
unknown type is first introduced in Typescript 3.0. It is also another top type in Typescript. As per official docs:
unknown is the type-safe counterpart of any
It is similar to any because it can accept all types of values. It enforces a bit more restriction than any because you cannot perform any action on unknown typed variable without type assertion or narrowing it to more specific type.
Syntax
Implications of assigning unknown type
- You can assign anything to
unknown typed variable.
let a: unknown;
a = function () {};
a = false;
a = 1;
a = "Universe";
a = [1, 2, 3, 4];
a = null;
unknown typed variable is only assignable to unknown or any type.
let a: unknown;
const b: unknown = a;
const c: any = a;
/**
* Typescript will throw an error if you
* try to assign `unknown` type to any other
* type except `unknown` and `any`
*/
const d: string = a; // error: Type 'unknown' is not assignable to type 'string'
const e: number = a; // error: Type 'unknown' is not assignable to type 'number'
const f: boolean = a; // error: Type 'unknown' is not assignable to type 'boolean'
const g: Function = a; // error: Type 'unknown' is not assignable to type 'Function'
- You can not perform any operations without narrowing or type assertion.
let a: unknown;
/**
* Typescript will throw an error if you
* type to perform an action directly without
* narrowing
*/
a.getUserName(); // error: Object is of type 'unknown'
a(); // error: Object is of type 'unknown'
a.b = 1; // error: Object is of type 'unknown'
- Only equality operators are allowed with
unknown
const a: unknown = 1;
const isFive = a = 5;
const is5 = a = 5;
const isNotFive = a = 5;
const isNot5 = a = 5;
const isGreater Than Five = a > 5; // error: Object is of type 'unknown'
const isLess Than Five = a < 5; // error: Object is of type 'unknown'
const increment = a + 1; // error: Object is of type 'unknown'
const multiply = a * 2; // error: Object is of type 'unknown'
- You cannot create rest from
unknown type
function anyFunction(x: unknown) {
let { ...a } = x; // Error
}
- Union with
unknown and other types produces unknown type with an exception of union with any which produces any type
type UnionType1 = unknown | null; // unknown
type UnionType2 = unknown | undefined; // unknown
type UnionType3 = unknown | boolean; // unknown
type UnionType4 = unknown | string[] | null; // unknown
/**
*
* Union with any produces `any` type instead of `unknown`
*
type UnionType5 = unknown | any; // any
- When taking Intersection with
unknown, it is absorbed by other types.
type IntersectionType1 = unknown & null; // null
type IntersectionType2 = unknown & undefined; // undefined
type IntersectionType3 = unknown & null & undefined; // never
type IntersectionType4 = unknown & string; // string
type IntersectionType5 = unknown & any; // any
Perform operation on unknown type
Before performing any operation on unknown type we need to narrow it down using typeof or instanceof operator. We can also use type assertions with as or we can provide a custom function which acts as type guard
Using typeof
const printLength = (input: unknown) ⇒ {
// const length = input.length; Error: Object is of type 'unknown'.
if(typeof input = 'string') {
console.log(input.length);
}
}
Using instanceof
class Animal {
speak() {
console.log('implement speak method');
}
}
class Cat extends Animal {
speak() {
console.log('Meowwwwww') ;
}
const talk = (input: unknown) ⇒ {
// input.speak() // Error: Object is of type 'unknown'.
if(input instanceof Animal) {
input. speak()
}
}
talk (new Cat()). // Meowwwwww
Using type assertion
const capitalize = (input: unknown): string ⇒ {
const strInput: string = input as string;
const [firstLetter, ...rest] = strInput.split('');
return firstLetter.toUpperCase() + rest.join(' ');
}
console.log(capitalize('Delhi'))
/***
* there is possibility of runtime error as typescript
* assumes that you know your system very well and
* will not pass anything which causes an error
* like in below case
*/
console.log(capitalize(['banglore']))
// Error: strInput.split is not a function or its return value is not iterable
Example
LocalStorage
Following is an example of saving data into localStoarage. As anything can be saved in localStorage that's why the type of data is unknown.
const saveData = (key: string, data: unknown) => {
localStorage.setItem(key, JSON.stringify(data));
};
Params to a http request
const param: Record<string, unknown> = {
name: "Morty",
age: 15,
location: {
name: "Earth (C-137)",
},
};
Word of wisdom
Use unknown before trying to use any
References