Home > Enterprise >  In TypeScript's type system how can I check for freshness for typed parameters?
In TypeScript's type system how can I check for freshness for typed parameters?

Time:11-16

Building off of this question (no I do not have a grand scheme, I simply thought of this after getting my question answered.):

I have the following:

type LengthOfString<T extends string, L extends any[] = []> = 
  T extends `${string}${infer R}` 
    ? LengthOfString<R, [...L, string]>
    : L["length"]

type StringOfLength<T extends string, N extends number> = LengthOfString<T> extends N ? T : never

function isStringOfLength<T extends string, N extends number>(str: T, len: N): str is StringOfLength<T, N> {
  return str.length === len
}

function takeString<T extends string>(str: StringOfLength<T, 5>) {
  if (!isStringOfLength<T, 5>(str, 5)) throw Error("String length !== 5")
  console.log(str)
}

// Works! (fresh)
takeString("22222")

// As expected, errors (fresh)
takeString("2222")

let str = "22222"

// Errors -- how can I avoid this? (str isn't fresh)
takeString(str)

As shown in the code, I have a run-time type guard isStringOfLength that is meant to throw runtime errors for non-fresh variables. However, I am unsure about how to change my code to accept these non-fresh values, while type-checking against the fresh strings as shown with the first two calls.

TS Playground

CodePudding user response:

If we want to accept

  • valid literal string types

  • just the string type

but don't want to accept

  • invalid literal string types

we can add an additional check to the function parameter.

function takeString<
  T extends string
>(str: string extends T ? T : StringOfLength<T, 5>) {
  if (!isStringOfLength<T, 5>(str, 5)) throw Error("String length !== 5")
  console.log(str)
}

We accept T as an argument to the function if string extends T. But if T is a string literal, the argument's type must be StringOfLength<T, 5>. This might seem counter-intuitive as we check if string extends T before we even infer T but the compiler makes it work for us.

// Works! (fresh)
takeString("22222")

// As expected, errors (fresh)
takeString("2222")

let str = "22222"
// Does not error anymore
takeString(str)

Playground

CodePudding user response:

You can use let str = "22222" as const to tell TS that str will be the constant string 22222. But I don't think it'll be terribly useful.

What you need here is to let TS know that str is a string of length 5. You can use this, but now you have redundant checks, just keep that in mind. str is StringOfLength<T, N> in the function's return type is the important part here.

let str = "22222" 

if (isStringOfLength(str, 5))
takeString(str)

playground

  • Related