Reputation: 1
I'm trying to refine the type of an optional value in TypeScript. I want to precisely determine the type of the 'value' based on the 'text' property of the 'option' in TypeScript.
typescript
import "./styles.css";
// type SelectOption = {
// text: 'a' | 'b';
// value: SelectOption[text] === 'a' ? string : number
// };
type SelectOption =
| {
text: "a";
value: string;
}
| {
text: "b";
value: number;
};
type MultySelectOption =
| {
text: "c";
value: boolean;
}
| {
text: "d";
value: Record<string, string>;
};
type OptionsBy<T extends string> = T extends "select"
? SelectOption[]
: T extends "multiSelect"
? MultySelectOption[]
: never;
export default function App() {
const options: OptionsBy<"select"> = [];
const numberValue = options?.find(({ text }) => text === "b")?.value;
//numberValue: string | number | undefined
const sum = () => {
return numberValue + 10;
};
return <div className="App"></div>;
}
I want to narrow down the type of numberValue from string | number | undefined to just number | undefined. How can I achieve this in the provided code?
codesandbox link http://codesandbox.io/p/sandbox/vigorous-ishizaka-xsn7cq?file=%2Fsrc%2FApp.tsx%3A1%2C1-40%2C1
Upvotes: 0
Views: 53
Reputation: 735
You can simply use an if statement, to check if the type of your numberValue is not string.
if (typeof numberValue !== "string") {
// In here the type of numberValue will be interpreted as `number | undefined`
}
In addition, and I assume you don't want that you can just remove the part of code that is responsible for giving numberValue a type of string.
{
text: "a";
value: string;
}
Even trough this answers your question, I much prefer what @cj0x39e did and I would advice you to just rewrite your types. And even trough I don't know what you are writing it feels like you might want to use classes for your specific use case but this is just food for thoughts.
Upvotes: 1
Reputation: 682
You can use the options above, but also, if you are working on the code and know beyond the shadow of doubt that the options code will always return a number or undefined (not the greatest Idea, but it will work) then you can do this:
export default function App() {
const options: OptionsBy<"select"> = [];
let numberValue = options?.find(({ text }) => text === "b")?.value as
| number
| undefined;
//numberValue: string | number | undefined
const sum = () => {
if (numberValue) {
return numberValue + 10;
} else {
return "numberValue was undefined"
}
};
Upvotes: 0
Reputation: 171
You can easily achieve this with the following code without any doubt. In my opinion, 'generic types' is not the best choice for this scenario.
interface Option {
text: string;
value: string | number;
}
interface NumberOption extends Option {
value: number;
}
interface StringOption extends Option {
value: string;
}
Upvotes: 1