TypeScript: Mastering the Unknown with Advanced Type Utilities
In TypeScript, unknown is a powerful type that represents any value. However, it comes with the caveat that you must perform type checking before you can operate on a variable of type unknown. This challenge focuses on creating robust type utility functions to safely narrow down and work with unknown types, enhancing code safety and developer experience.
Problem Description
Your task is to implement a set of TypeScript utility functions that help safely narrow down and work with values of type unknown. These helpers should provide a more ergonomic and type-safe way to handle potentially unknown data, such as data fetched from external APIs or user input.
You will need to implement the following functions:
-
isString(value: unknown): value is string- Checks if the given
valueis a string. - Should return
trueif it's a string,falseotherwise.
- Checks if the given
-
isNumber(value: unknown): value is number- Checks if the given
valueis a number. - Should return
trueif it's a number,falseotherwise.
- Checks if the given
-
isBoolean(value: unknown): value is boolean- Checks if the given
valueis a boolean. - Should return
trueif it's a boolean,falseotherwise.
- Checks if the given
-
isObject(value: unknown): value is object- Checks if the given
valueis a plain JavaScript object (i.e., not null, and of type 'object'). - Should return
trueif it's a plain object,falseotherwise.
- Checks if the given
-
isArray(value: unknown): value is any[]- Checks if the given
valueis an array. - Should return
trueif it's an array,falseotherwise.
- Checks if the given
-
is dalam (value: unknown, validator: (v: unknown) => boolean): value is T(whereTis inferred from the validator)- A generic function that takes a
valueofunknownand avalidatorfunction. - The
validatorfunction should acceptunknownand return a type predicate (e.g.,v is MySpecificType). - This function should return
trueif thevalidatorreturnstruefor thevalue, andfalseotherwise. It should also correctly infer and use the specific typeTthat the validator narrows down to.
- A generic function that takes a
Examples
Example 1: Basic Type Checks
let data: unknown = "hello";
if (isString(data)) {
console.log(data.toUpperCase()); // Output: HELLO
}
data = 123;
if (isNumber(data)) {
console.log(data.toFixed(2)); // Output: 123.00
}
data = true;
if (isBoolean(data)) {
console.log(!data); // Output: false
}
data = { name: "Alice" };
if (isObject(data)) {
console.log(data.name); // Output: Alice (Requires careful handling in TS for properties)
}
data = [1, 2, 3];
if (isArray(data)) {
console.log(data.length); // Output: 3
}
Example 2: Using is for Complex Types
interface User {
id: number;
name: string;
}
const isUser = (value: unknown): value is User => {
return (
isObject(value) &&
value !== null && // Redundant with isObject but good for clarity
'id' in value && typeof value.id === 'number' &&
'name' in value && typeof value.name === 'string'
);
};
let potentialUser: unknown = { id: 1, name: "Bob" };
if (is dalam (potentialUser, isUser)) {
console.log(`User ID: ${potentialUser.id}, Name: ${potentialUser.name}`); // Output: User ID: 1, Name: Bob
}
potentialUser = { id: 2, username: "Charlie" }; // Missing 'name' property
if (is dalam (potentialUser, isUser)) {
// This block will not execute
} else {
console.log("Not a valid User object."); // Output: Not a valid User object.
}
Example 3: Edge Cases
// isObject: null is of type 'object' in JS, but we want plain objects
let nullValue: unknown = null;
console.log(isObject(nullValue)); // Output: false
// isArray: Non-array objects should not be considered arrays
let notAnArray: unknown = { length: 3 };
console.log(isArray(notAnArray)); // Output: false
// isNumber: NaN is a number, but might need specific handling elsewhere
let nanValue: unknown = NaN;
console.log(isNumber(nanValue)); // Output: true
// is dalam: Validator returning false
const isPositiveNumber = (v: unknown): v is number => isNumber(v) && (v as number) > 0;
let negValue: unknown = -5;
if (is dalam (negValue, isPositiveNumber)) {
// This block will not execute
} else {
console.log("Value is not a positive number."); // Output: Value is not a positive number.
}
Constraints
- All implemented helper functions must use type predicates (
value is Type) to effectively narrow down types within TypeScript. - The
isObjectfunction should specifically excludenullfrom being considered a valid "object" for this challenge's purpose. - The
isfunction must correctly infer the generic typeTbased on the providedvalidator's type predicate. - Performance is not a primary concern, but solutions should be reasonably efficient. Avoid overly complex or computationally expensive checks where simpler ones suffice.
Notes
- Remember that JavaScript's
typeof nullreturns'object'. YourisObjectimplementation will need to handle this. - For the
isfunction, consider how you can leverage TypeScript's conditional types and generic inference to make it work seamlessly with any valid type predicate. - Think about how these helpers can be used in real-world scenarios, like parsing JSON data or validating user inputs.
- The
is dalamfunction is a placeholder for a more descriptive name you might choose, likeisOf,satisfies, orcheck. Choose a clear and concise name.