00300-hard-string-to-number

Back

思路:

  1. 需要解析 0-9 的数字,定义联合类型 NumberUnion
S extends `${infer L extends NumberUnion}${infer Rest}` 
// 这样L便可以推断成数字了(0-9)。
  1. 需要实现加法 AddNumber, 利用数组实现
type AddNumber<N extends number, M extends number> = number extends N
  ? number extends M
    ? 0
    : 0
  : [...NumberToArr<N>, ...NumberToArr<M>]["length"] & number;
  1. 需要实现 乘10 MultiplyTen, 利用数组实现,写得比较死板
type MultiplyTen<N extends number> = number extends N
  ? 0
  : [
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>
    ]["length"] & number;
// 单个数字类型
type NumberUnion = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
// 将数字转为相同大小的数组  NumberToArr<3>  // 结果是[1,1,1]
type NumberToArr<N extends number, Count extends 1[] = []> = Equal<
  N,
  Count["length"]
> extends true
  ? Count
  : NumberToArr<N, [...Count, 1]>;

// 将两个数字相加 AddNumber<1, 2> // 3
type AddNumber<N extends number, M extends number> = number extends N
  ? number extends M
    ? 0
    : 0
  : [...NumberToArr<N>, ...NumberToArr<M>]["length"] & number;

// 将某个数字乘10   MultiplyTen<10> // 100 
type MultiplyTen<N extends number> = number extends N
  ? 0
  : [
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>,
      ...NumberToArr<N>
    ]["length"] &
      number;

type ToNumber<
  S extends string,
  Res extends number = 0
> = S extends `${infer L extends NumberUnion}${infer Rest}`
  ? ToNumber<Rest, AddNumber<L, MultiplyTen<Res>>>
  : S extends ""
  ? Res
  : never;

Solution by Vampirelee #32618

type TStringToNumber<T> = T extends `${infer N extends number}` ? N : never;

type ParseInt<T extends string, Temp extends string = ''> = T extends `${infer First}${infer Rest}` ? (
    First extends `${infer N extends number}` ? (
        N extends number ? ParseInt<Rest, `${Temp}${N}`> : Temp
    ) : TStringToNumber<Temp>
) : TStringToNumber<Temp>;

type Test1 = ParseInt<'74,2'> //74
type Test2 = ParseInt<'3f'> //3
type Test3 = ParseInt<'hello'> //never

Solution by KarenGrigoryan1999 #30760

type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never

Solution by Chan-Yuxi #30526

// your answers
type ToNumber<S extends string, T extends any[] = []> = S extends `${number}` ? S extends `${T["length"]}`
  ? T["length"]
  : ToNumber<S, [...T, any]> : never

Solution by AAA611 #28823

type ToNumber<S extends string> = S extends `${infer T extends number}`?T:never

Solution by jiangshanmeta #27908

There is the implementation I use in type-plus.

export type StringToNumber<S extends string, Fail = never> = S extends `-0`
	? 0
	: S extends `${infer W}.0`
	? StringToNumber<W>
	: S extends `${infer W}.${infer F}0`
	? StringToNumber<`${W}.${F}`>
	: S extends `${infer N extends number}`
	? N
	: Fail

It is used in part of StringToNumeric<S> that works with both number and bigint.

It handles positive, negative, floating point numbers, and special cases (e.g. -0 and 1.0).

Here are the unit test

🍻

Solution by unional #27561

type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never

Solution by smileboyi #27498

type Num = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

type FuNum<S> = S extends `-${infer R}` ? R : S
type isFu<S> = FuNum<S> extends never ? S : FuNum<S>

type isNum<S> = S extends `${infer R}${infer X}`
  ?
  (R extends `${Num[number]}`
    ?
    (X extends '' ? true : isNum<X>)
    : false)
  : never

type GetNum<S> = isNum<isFu<S>> extends true ? S : never

type A<S, Arr extends any[] = []> = S extends `${Arr['length']}` ? Arr['length'] : A<S, [S, ...Arr]>

type ToNumber<S extends string> = A<FuNum<GetNum<S>>>



// this's  false         (  +number  To  -number  ,    i  don't  konw     HELP ME!    )
 Expect<Equal<ToNumber<'-27'>, -27>>

Solution by Acoooooooer #26832

300 - String To Number

As of TypeScript 4.8 this has a super trivial one-line solution, but before that it was quite intense. The solutions on this video are more like an archaeological dig than anything else.

🎥 Video Explanation

Release Date: 2023-04-10 19:00 UTC

String To Number

🔢 Code

// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'

type A1 = ToNumber<'0'>;
type B1 = 0;
type C1 = Expect<Equal<A1, B1>>;

type A2 = ToNumber<'5'>;
type B2 = 5;
type C2 = Expect<Equal<A2, B2>>;

type A3 = ToNumber<'12'>;
type B3 = 12;
type C3 = Expect<Equal<A3, B3>>;

type A4 = ToNumber<'27'>;
type B4 = 27;
type C4 = Expect<Equal<A4, B4>>;

type A5 = ToNumber<'18@7_$%'>;
type B5 = never;
type C5 = Expect<Equal<A5, B5>>;

// // ============= Your Code Here =============
// // /** TypeScript 4.8: https://devblogs.microsoft.com/typescript/announcing-typescript-4-8-beta/#infer-types-template-strings */
// type ToNumber<T> =
//   T extends `${infer N extends number}`
//   ? N
//   : never;

// type ToNumber<
//   T extends string,
//   Count extends '🧮'[] = []
// > =
//   T extends `${number}`
//   ? T extends `${Count["length"]}`
//     ? Count["length"]
//     : ToNumber<T, [...Count, '🧮']>
//   : never;

// // ================== NOPE ==================
// // Type instantiation is excessively deep
// //   and possibly infinite.ts(2589)
// type ToNumber<
//   T extends string,
//   Count extends 1[] = []
// > =
//   T extends `${Count["length"]}`
//   ? Count["length"]
//   : ToNumber<T, [...Count, 1]>;


// ============== Alternative ==============
// @uid11
type Num = readonly 0[]

/**
 * NumToNumber<Num3> = 3, where Num3 = [0, 0, 0]
 */
type NumToNumber<N extends Num> = N['length']

type Digit =
  | '0' | '1' | '2' | '3' | '4'
  | '5' | '6' | '7' | '8' | '9'

/**
 * MultiplyToDigit<'3', Num2> = Num6
 */
type MultiplyToDigit<D extends Digit, N extends Num> = {
  '0': [],
  '1': N,
  '2': [...N, ...N],
  '3': [...N, ...N, ...N],
  '4': [...N, ...N, ...N, ...N],
  '5': [...N, ...N, ...N, ...N, ...N],
  '6': [...N, ...N, ...N, ...N, ...N, ...N],
  '7': [...N, ...N, ...N, ...N, ...N, ...N, ...N],
  '8': [...N, ...N, ...N, ...N, ...N, ...N, ...N, ...N],
  '9': [...N, ...N, ...N, ...N, ...N, ...N, ...N, ...N, ...N],
}[D]

/**
 * Num1
 */
type One = [0]

/**
 * DigitToNum<'2'> = Num2
 */
type DigitToNum<D extends Digit> = MultiplyToDigit<D, One>

/**
 * Sum<Num2, Num3> = Num5
 */
type Sum<N extends Num, M extends Num> = [...N, ...M]

/**
 * MultiplyTo10<Num2> = Num20
 */
type MultiplyTo10<N extends Num> =
  MultiplyToDigit<
    '2',
    MultiplyToDigit<'5', N>
  >

/**
 * GetBase<'2'> = Num10, GetBase<'31'> = Num100
 */
type GetBase<Number extends string> =
  Number extends Digit
  ? MultiplyTo10<One>
  : Number extends `${string}${infer Rest}`
    ? MultiplyTo10<GetBase<Rest>>
    : never

/**
 * StrToNum<'13'> = Num13
 */
type StrToNum<Str extends string> = Str extends Digit
  ? MultiplyToDigit<Str, One>
  : Str extends `${infer D}${infer Rest}`
    ? D extends Digit
      ? Sum<
          MultiplyToDigit<D, GetBase<Rest>>,
          StrToNum<Rest>
        >
      : never
    : never

/**
 * ToNumber<'24'> = 24
 */
type ToNumber<S extends string> = NumToNumber<StrToNum<S>>

➕ More Solutions

For more video solutions to other challenges: see the umbrella list! https://github.com/type-challenges/type-challenges/issues/21338

Solution by dimitropoulos #25923

type ToNumber<S extends string> = S extends `${infer R extends number}` ? R : never;

Solution by E-uler #24767

// your answers
type ToNumber<S extends string> = S extends `${infer N extends number}`
? N
:never

Solution by walker-hzx #24604

type ToNumber<S extends string> = S extends `${infer IsNumber extends number}` ? IsNumber : never

Solution by NeylonR #24446

// your answers
type ToNumber<S extends string> = S extends `${infer F extends number}` ? F : never

Solution by snakeUni #23514

// your answers
type ToNumber<S extends string> = S extends `${infer R extends number}` ? R : never;

Solution by jxhhdx #22856

// your answers
type StringNumber = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'

type IsNumber<S extends string> =  
  S extends `${infer First}${infer Rest}` 
    ? First extends (StringNumber | '.' )
      ? IsNumber<Rest> 
      : false
    : never


type StringNumberArray<S extends string, R extends 1[] = [], MAX = 1000> = 
`${R['length']}` extends S 
  ? R 
  : MAX extends R['length'] 
    ? [] 
    : StringNumberArray<S, [...R, 1]>

type GetInt<S> = S extends `${infer Int}.${infer Rest}` ? Int : S

type ToNumber<S extends string> = IsNumber<S> extends true ? StringNumberArray<GetInt<S>> extends any[] ? StringNumberArray<GetInt<S>>['length'] : never : never

Solution by 437204933 #22296

type ToNumber<S extends string> =
  S extends `${infer N extends number}`
    ? N
    : never

Solution by drylint #22218

// your answers
type NumberMap = {
  "0": 0;
  "1": 1;
  "2": 2;
  "3": 3;
  "4": 4;
  "5": 5;
  "6": 6;
  "7": 7;
  "8": 8;
  "9": 9;
};
// 两数相加
type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;

// 两数相乘
// 2 x 5 = 2 + 2 + 2 + 2 
type Mul<
  A extends number,
  B extends number,
  Res extends number = 0,
  Cnt extends 0[] = []
> = Cnt["length"] extends B ? Res : Mul<A, B, Add<A, Res>, [0, ...Cnt]>;

type ToNumber<
  T extends string,
  Res extends number = 0
> = T extends `${infer F}${infer R}`
  ? F extends keyof NumberMap
    ? ToNumber<R, Add<Mul<Res, 10>, NumberMap[F]>>
    : never
  : Res;

Solution by acwink #22092

type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never;

Solution by Karamuto #21952

type StringToUnion<T extends string> = T extends `${infer First}${infer Rest}` ? First | StringToUnion<Rest> : never;
type Numbers = StringToUnion<'0123456789'>;

type TrimZero<T extends string | never> = T extends `${infer First}${infer Rest}`
  ? First extends '0'
    ? TrimZero<Rest>
    : T
  : T;

type T1 = TrimZero<'0012'>;
type T2 = TrimZero<'10012'>;
type T3 = TrimZero<'12'>;
type T4 = TrimZero<'0000'>;

type StringToNumberString<
  T extends string,
  Result extends string | never = never
> = T extends `${infer First}${infer Rest}`
  ? First extends Numbers
    ? StringToNumberString<Rest, `${[Result] extends [never] ? `${First}` : `${Result}${First}`}`>
    : Result
  : Result;

type N1 = StringToNumberString<'ABC'>;
type N2 = StringToNumberString<'0'>;
type N3 = StringToNumberString<'5'>;
type N4 = StringToNumberString<'12'>;
type N5 = StringToNumberString<'27'>;
type N6 = StringToNumberString<'18@7_$%'>;
type N7 = StringToNumberString<'27.12'>;
type N8 = StringToNumberString<'012'>;
type N9 = StringToNumberString<'000012'>;
type N10 = StringToNumberString<'0000.12'>;

type StringToIntNumberString<T extends string> = TrimZero<StringToNumberString<T>>;
type NI1 = StringToIntNumberString<'ABC'>;
type NI2 = StringToIntNumberString<'0'>;
type NI3 = StringToIntNumberString<'5'>;
type NI4 = StringToIntNumberString<'12'>;
type NI5 = StringToIntNumberString<'27'>;
type NI6 = StringToIntNumberString<'18@7_$%'>;
type NI7 = StringToIntNumberString<'27.12'>;
type NI8 = StringToIntNumberString<'012'>;
type NI9 = StringToIntNumberString<'000012'>;
type NI10 = StringToIntNumberString<'0000.12'>;

type ToNumber<T extends string, Result extends any[] = [], NT = StringToIntNumberString<T>> = [NT] extends [never]
  ? never
  : NT extends ''
  ? 0
  : `${Result['length']}` extends NT
  ? Result['length']
  : ToNumber<T, [...Result, 1]>;

type TN1 = ToNumber<'ABC'>;
type TN2 = ToNumber<'0'>;
type TN3 = ToNumber<'5'>;
type TN4 = ToNumber<'12'>;
type TN5 = ToNumber<'27'>;
type TN6 = ToNumber<'18@7_$%'>;
type TN7 = ToNumber<'27.12'>;
type TN8 = ToNumber<'012'>;
type TN9 = ToNumber<'000012'>;
type TN10 = ToNumber<'0000.12'>;
type TN11 = ToNumber<'A19'>;

Solution by zqiangxu #21512

// your answers
// new function 
type ToNumber<S extends string> = S extends `${infer F1 extends number}` ? F1 : never;

Solution by venusliang #20857

// your answers
type CreateArrBylen<T extends number,Arr extends any[] = []> = Arr['length'] extends T ? Arr : CreateArrBylen<T, Arr extends [...infer Rest] ? [...Rest,1] : []>
type IncreaseArr<T extends any[]> = T extends [...infer R] ? T['length'] extends 100 ? never : [...R,1]['length'] : never
type ToNumber<S extends string,Count extends number | never = 0> = Count extends never ? never : S extends `${Count}` ? Count : ToNumber<S,IncreaseArr<CreateArrBylen<Count>>>

Solution by YqxLzx #20656

type ToNumber<S extends string> = S extends `${infer A extends number}` ? A : never

Solution by zhaoyao91 #20491

// your answers
type TN<
  S extends string,
  arr extends unknown[] = []
> = `${arr["length"]}` extends `${S}` ? arr["length"] : TN<S, [...arr, 1]>;
// slow ? haha

Solution by fengjinlong #20240

// EASY
type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never

// SO SO SO stupid ANSWER
/**
 * 1. 提取 str num
 * 2. 将 str num =》 numer[]
 * 3. 按照数字的长度进行拼接;例如:100
 *  - 1) IdxToSinal【0】* Num1 +  IdxToSinal【1】* Num2 + 1IdxToSinal【2】* Num3
 */
type IdxToSinal = {
  0: 1,
  1: 10,
  2: 100,
  3: 1000,
  4: 10000
}
type strMap = {
  '0': 0
  '1': 1
  '2': 2
  '3': 3
  '4': 4
  '5': 5
  '6': 6
  '7': 7
  '8': 8
  '9': 9
}
// utils
type NumToArr<T, Ret extends any[] = []> = 0 extends 1 ? never : Ret['length'] extends T ? Ret : NumToArr<T, [...Ret, unknown]>
type Multiplication<S extends number, N extends number, SA extends any[] = NumToArr<S>, NA extends any[] = NumToArr<N>, Ret extends any[] = []> = NA extends [infer First, ...infer Rest] ? Multiplication<S, N, SA, Rest, [...Ret, ...SA]> : Ret

type res = Multiplication<10, 5>['length']

// step1 & step2
type StrToNumberList<S extends string, Ret extends number [] = []> = S extends `${infer First extends keyof strMap}${infer Rest}` ? StrToNumberList<Rest, [...Ret,strMap[First]]> : Ret 

// step3
type getNumFromIdxMap<T extends any[]> = T['length'] extends keyof IdxToSinal ? IdxToSinal[T['length']] : 0
type AccNum<T extends number[],CurIdx extends any[] = [], Ret extends any[] = []> = T extends [ ...infer Rest extends number[],infer First extends number] ? AccNum<Rest, [...CurIdx, unknown], [...Ret, ...Multiplication<getNumFromIdxMap<CurIdx>,First>]> : Ret['length']
type ToNumber<S extends string> = S extends `${number}` ? AccNum<StrToNumberList<S>> : never

Solution by Rebornjiang #20237

type Numbers = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'

type CheckString<S> = S extends `${infer First}${infer Tail}`
  ? First extends Numbers
    ? CheckString<Tail> : false
  : true

type ToNumber<S extends string, Arr extends any[] = []> = CheckString<S> extends false
  ? never : S extends `${Arr['length']}`
    ? Arr['length'] : ToNumber<S, [1, ...Arr]>

Solution by Zhukov87 #19673

type StringToUnion<T extends string> = T extends `${infer F}${infer R}` ? F | StringToUnion<R> : never;
type Number = StringToUnion<'0123456789'>;
type ToNumber<S extends string, Arr extends 0[] = []> = StringToUnion<S> extends Number ?
 `${Arr['length']}` extends S ?
    Arr['length'] :
    ToNumber<S, [...Arr, 0]> :
  never;

Solution by CallMeSaltyF1sh #19440

type SingleNumber = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';

type IsCanConvert<S extends string> = S extends `${infer F}${infer R}` ? (F extends SingleNumber ? IsCanConvert<R> : false) : true;

type ToNumber<S extends string, N extends any[] = []> = IsCanConvert<S> extends true ? (`${N['length']}` extends S ? N['length'] : ToNumber<S, [...N, 1]>) : never;

Solution by CaoXueLiang #19036

type ArrayBase = {
  "0": [],
  "1": [unknown],
  "2": [unknown, unknown],
  "3": [unknown, unknown, unknown],
  "4": [unknown, unknown, unknown, unknown],
  "5": [unknown, unknown, unknown, unknown, unknown],
  "6": [unknown, unknown, unknown, unknown, unknown, unknown],
  "7": [unknown, unknown, unknown, unknown, unknown, unknown, unknown],
  "8": [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown],
  "9": [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown]
}

type NumStrToArray<T extends string, A extends unknown[] = []> = T extends `${infer F}${infer R}`
  ? F extends keyof ArrayBase
    ? NumStrToArray<R, [...A, ...A, ...A, ...A, ...A, ...A, ...A, ...A, ...A, ...A, ...ArrayBase[F]]>
    : never
  : A

type num = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type IsNumber<T> =  T extends `${num}${infer S}`
  ? IsNumber<S>
  : T extends ''
    ? true
    : false

type ToNumber<S extends string> = IsNumber<S> extends true
  ? NumStrToArray<S>['length']
  : never

type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never

Solution by milletlovemouse #18825

// your answers
type ToNumber<S extends string> = S extends `${infer N extends number}` ? N : never;

Solution by jiaaoMario #17355

// your answers

type NumberMap = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

type GetResult<S extends string, Result extends number[] = []> = S extends `${infer F}${infer R}`
  ? F extends keyof NumberMap
    ? GetResult<R, [...Result, NumberMap[F] & number]>
    : never
  : Result

type NumberToTuple<N extends number, Result extends 0[] = []> = Result['length'] extends N
  ? Result
  : NumberToTuple<N, [...Result, 0]>

type GetTenTimes<T extends any[] = []> = [
  ...T, ...T, ...T, ...T, ...T,
  ...T, ...T, ...T, ...T, ...T
]

type GetLength<T extends number[], Result extends unknown[] = []> = T extends [infer F extends number, ...infer R extends number[]]
  ? GetLength<R, [...GetTenTimes<Result>, ...NumberToTuple<F>]>
  : Result['length']

type ToNumber<S extends string, R extends number[] = GetResult<S>> = [R] extends [never]
  ? never
  : GetLength<R>

Solution by humandetail #16431