lucid-jasper-8kyn6t - CodeSandbox
/* Definition Part */
import { parseOptionalNfValue } from "./parse-nf-value";
import { parseNumberWithSuffix } from "./parse-number-with-suffix";
export type ParseNfValue = string | number;
export type ParamOfParseNumberWithSuffix = Parameters<
typeof parseNumberWithSuffix
>;
export type NfOptions = ParamOfParseNumberWithSuffix[1];
type Optional<T> = T | undefined;
export class Nf<T extends number | string | undefined | null> {
number: T extends null | undefined ? Optional<number> : number;
currency: T extends null | undefined ? Optional<string> : string;
currencyWithWon: T extends null | undefined ? Optional<string> : string;
nf: NfOptions;
constructor(nf: NfOptions, nfValue?: T) {
this.nf = nf;
// I use as
this.number = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<number>
: number;
this.currency = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<string>
: string;
this.currencyWithWon = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<string>
: string;
}
}
Intention: I want the return type of the nf class method to vary based on the optionality of the value. like below
/* Usage Part */
/* type test*/
const optionalNf1: number | undefined = undefined;
export const test1Nf = new Nf('0f', optionalNf1);
test1Nf.currency; // test1Nf should be inferred as a type of number | undefined.
const currencyWithSuffix1 = test1Nf.toCurrencyWithSuffix('원');
currencyWithSuffix1; // currencyWithSuffix1 should be inferred as a type of string | undefined.
const optionalNf2 = 1;
export const test2Nf = new Nf('0f', optionalNf2);
test2Nf.currency; // test2Nf should be inferred as a type of string
const currencyWithSuffix2 = test2Nf.toCurrencyWithSuffix('원');
/* #issue
* currencyWithSuffix2 should be inferred as a type of string
* but currencyWithSuffix2 is inferred as string | undefined
* I want the return type to be inferred as string.
*/
currencyWithSuffix2;
/* Definition Part */
import { parseOptionalNfValue } from "./parse-nf-value";
import { parseNumberWithSuffix } from "./parse-number-with-suffix";
export type ParseNfValue = string | number;
export type ParamOfParseNumberWithSuffix = Parameters<
typeof parseNumberWithSuffix
>;
export type NfOptions = ParamOfParseNumberWithSuffix[1];
type Optional<T> = T | undefined;
export class Nf<T extends number | string | undefined | null> {
number: T extends null | undefined ? Optional<number> : number;
currency: T extends null | undefined ? Optional<string> : string;
currencyWithWon: T extends null | undefined ? Optional<string> : string;
nf: NfOptions;
constructor(nf: NfOptions, nfValue?: T) {
this.nf = nf;
this.number = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<number>
: number;
this.currency = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<string>
: string;
this.currencyWithWon = parseOptionalNfValue(nf, nfValue) as T extends
| null
| undefined
? Optional<string>
: string;
}
// TODO: implement toNfString
// eslint-disable-next-line @typescript-eslint/no-unused-vars
toNfString(nf?: NfOptions): string {
return this.nf;
}
toCurrencyWithSuffix(suffix: string): string | undefined {
if (this.currency == null) {
return;
}
return `${this.currency}${suffix}`;
}
}
Our team's server use utilizes grpc and protobuf. Due to internal history, we can't use grpc directly on the web client and instead proxy grpc through REST. Given this background, all number types provided by the internal server API are delivered in the form of a number string, like {"amount0f: "10000"}
nf suffix?💡In the web client, we use the nf format of number strings in various ways.
To facilitate such flexible usage, I am planning to create an 'nf' class and introduce methods that return commonly used values. However, there's an issue when dealing with optional values. To reduce unnecessary if statements in usage, when nf is not optional, I want it to infer a fixed return type T, and when it's optional, we aim for it to infer as T | undefined.
To achieve the above goal, by utilizing function overloading, I successfully implemented the function as described below.
import { parseNumberWithSuffix } from 'parse-number-with-suffix';
import { isNil } from 'remeda';
export function parseOptionalNfValue<T extends number | string>(nf: string, nfValue: T): number;
export function parseOptionalNfValue(
nf: string,
nfValue: undefined | null | string | number
): number | undefined;
export function parseOptionalNfValue(
nf: string,
nfValue?: number | string | undefined | null
): number | undefined {
if (isNil(nfValue)) {
return undefined;
} else {
return parseNumberWithSuffix(String(nfValue), nf);
}
}
/* type test*/
const optionalNf1: number | undefined = undefined;
export const test1 = parseOptionalNfValue('0f', optionalNf1);
test1; // test1 should be inferred as a type of number | undefined.
const optionalNf2 = 1;
export const test2 = parseOptionalNfValue('0f', optionalNf2);
test2; // test2 should be inferred as a type of number