Customize a hooks
import { useLocation } from 'react-router-dom'; // Define the QueryParamsKey type as string or Record<string, string>type QueryParamsKey = string | Record<string, string>; // Define the return typetype QueryParamsResult = Record<string, string>; const useGetQueryParams = (key: QueryParamsKey): QueryParamsResult => { const location = useLocation(); const queryParams = new URLSearchParams(); // Initialization returns result let values: QueryParamsResult = {}; // Type guard, determine whether the key is a string if (typeof key === 'string') { values[key] = (key) || ''; } else if (typeof key === 'object' && key !== null) { // Iterate through each key in the object and get the corresponding query parameters (key).forEach((k) => { // Use the value in the key as the key to query the parameter values[k] = (key[k]) || ''; }); } return values; }; export default useGetQueryParams;
question:
useconst {templateId} = useGetQueryParams(['templateId'])
The prompt type "QueryParamsResultValues" does not exist.
explain:
useGetQueryParams
The return type of the function is defined asQueryParamsResultValues
This is a joint typestring | Record<string, string>
. When you calluseGetQueryParams(['templateId'])
When the return value should be aRecord<string, string>
。
The problem is that TypeScript cannot determine when compiled the return type of the function isstring
stillRecord<string, string>
, because it depends on the runtime behavior of the function. So, when you try to deconstruct the return value and accesstemplateId
TypeScript throws an error when a property is present because it is not sure whether the returned object is an object.
solve
Use function declarative function overload to let TypeScript infer different return types based on different parameter types
import { useLocation } from 'react-router'; type QueryParamsKeys = string | string[]; type QueryParamsResult = Record<string, string | null>; // Function overload declarationfunction useGetQueryParams(key: string): string | null; function useGetQueryParams(keys: string[]): QueryParamsResult; // Function implementationfunction useGetQueryParams(keys: QueryParamsKeys): string | null | QueryParamsResult { const location = useLocation(); const queryParams = new URLSearchParams(); // Handle a single key if (typeof keys === 'string') { return (keys); } // Handle key array else if ((keys)) { const values: QueryParamsResult = {}; ((k) => { values[k] = (k); }); return values; } throw new Error('Invalid keys argument'); } export default useGetQueryParams;
Although we have multiple overload signatures, there is only one function implementation. Inside this implementation, we handle all possible calling situations. When the function is called, the TypeScript compiler infers which overload signature is used based on the provided parameter type and overload signature, and checks the consistency of the return type based on it.
Therefore, unlike ordinary JavaScript, the function overloading of TypeScript does not really provide multiple different function implementations at runtime, but rather checks and infers the type of function call at compile time. In actual JavaScript code, there is still only one function body
Notice
In TypeScript, you cannot use arrow functions to overload functions. Function overloading is a feature of TypeScript that allows you to provide multiple function type definitions for the same function. However, this feature is limited to traditional function declarations and does not apply to arrow functions, because arrow functions cannot be named.
// Function declaration, can be reloadedfunction greet(name: string): string; function greet(age: number): string; function greet(single: boolean): string; function greet(value: string | number | boolean): string { // Actual implementation logic return `Hello, ${value}`; }
This is because when JavaScript runs, there is no concept of function overloading; it is provided by TypeScript at the type system level to check and infer the types of function calls at compile time. Function overloading requires a series of function declarations with the same name to implement, while arrow functions are anonymous, so overloading cannot be used.
If you want to simulate the behavior of function overloading while using arrow functions, you can define a type with multiple signatures and assign the type to a variable, and assign the arrow function to this variable:
// Define function typetype GreetFunction = { (name: string): string; (age: number): string; (single: boolean): string; }; // Define the variable and assign the arrow function, but in fact this is not a function overloadconst greet: GreetFunction = (value: string | number | boolean): string => { // Actual implementation logic return `Hello, ${value}`; };
The above method is not a real function overload, it just simulates the behavior of function overloading. Although this is not a real overload, it can still return different results based on the type of parameter passed in at the call.
Why does arrow function not support overloading:
- Anonymous nature: Arrow functions are usually anonymous, they have no function names. When defining function overloading in TypeScript, multiple declarations with the same name are required to express different call signatures.
- Statement method: In TypeScript, function overloading is achieved by listing multiple function declarations with the same name. This is done by providing multiple type signatures to the same function name at the type level. As an expression, arrow functions do not have the ability to declare multiple types of signatures.
Why use function declarations for overloading in TypeScript:
- Syntax Support: TypeScript's function overload syntax is specially designed for traditional function declarations.
- Compiler inference: The TypeScript compiler can infer the return type of function calls based on overloaded signatures, which provides developers with type safety.
This is the article about the implementation method of TypeScript using function overload to determine the return type. For more related content related to TypeScript to determine the return type, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!