【问题标题】:How to make TypeScript distinguish between a template literal string and any other string?如何使 TypeScript 区分模板文字字符串和任何其他字符串?
【发布时间】:2021-06-02 15:36:15
【问题描述】:

Playground link

我有一个函数,它接受第二个参数,当第一个参数是与模板文字字符串匹配的字符串时,需要。然而,似乎即使 TypeScript 知道第一个参数是这样一个字符串,它仍然匹配采用常规字符串的重载:

type KnownString = `known${string}`;
type AnyString = string;

function doSomethingWithString(arg1: KnownString, onlyRequiredForKnownString: boolean): string;
function doSomethingWithString(arg1: AnyString, onlyRequiredForKnownString?: boolean): string;
function doSomethingWithString(arg1: KnownString | AnyString, onlyRequiredForKnownString?: boolean): string {
    return "The second argument is only required when `arg1` is a KnownString";
}

const thisIsAKnownString = "knownstring" as KnownString;

// @ts-expect-error Since TS knows that arg1 is a KnownString, it should require the second parameter:
doSomethingWithString(thisIsAKnownString);

这当然是有道理的,因为它一个常规字符串。有没有办法告诉重载匹配任何常规字符串 except KnownString

(我 did try with a conditional type 喜欢 this similar question 中的建议,但这似乎不适用于模板文字字符串。)

【问题讨论】:

  • 您的函数难道不能通过第二个重载接收“已知”字符串吗?我的意思不是在编译时,我的意思是在运行时,当省略第二个参数时,函数仍然必须能够处理这样的字符串。在这种情况下它会抛出错误吗?
  • @kaya3 是的,在我的实际代码中,还有一个运行时检查,以防不使用 TypeScript 的开发人员调用它,但我希望 TypeScript 开发人员在没有潜在错误的情况下得到通知必须在正在运行的应用程序中触发此代码路径。

标签: typescript


【解决方案1】:

我相信你不需要重载你的函数。

考虑下一个例子:

type IsKnown<T extends string> = T extends `known${string}` ? true:false

type Validator<T extends boolean> = 
  T extends true 
  ? [] 
  : [boolean];

function validation<Str extends string>(arg1: Str, ...validation: [...Validator<IsKnown<Str>>]): string {
    return "The second argument is only required when `arg1` is a KnownString";
}

const knownstring = "knownstring";

validation(knownstring); // does not require second argument
validation('unknown'); // requires second argument

Playground

请记住,function foo(a:number,...args:[]){} 的计算结果为 function foo(a:number){}

您可以在我的blog 中找到有关此方法的更多信息

考虑到您可以编写验证器:

type IsKnown<T> = T extends `known${string}` ? true : false

type StringNumber<T> = T extends `${number}` ? true : false


type Validator<T extends boolean> =
  T extends true
  ? []
  : [boolean];


function validation<Str extends string>(arg1: Str, ...validation: Validator<StringNumber<Str>> | Validator<IsKnown<Str>>): string {
  return "The second argument is only required when `arg1` is a KnownString";
}

const knownstring = "knownstring";

validation(knownstring); // does not require second argument
validation('2234'); // does not require second argument, because it is number
validation('wer'); //  requires second argument, because it is number

【讨论】:

  • 嗯,这当然是一个有趣的方法。让我看看当我在我的实际代码库中尝试时会发生什么。
  • 太棒了。我不太喜欢有些难以理解的复杂类型(尤其是因为我正在研究一个库),但它确实工作。谢谢!
  • 不客气。我试图将类型验证拆分为几个较小的类型实用程序,所以我希望它可读
【解决方案2】:

我确实找到了另一种具有函数重载和更简单类型的解决方案:

type KnownString = `known${string}`;
type AnyString = string;
type NotKnown<T extends AnyString> = T extends KnownString ? never : T;

function doSomethingWithString(arg1: KnownString, onlyRequiredForKnownString: boolean): string;
function doSomethingWithString<T extends AnyString>(arg1: T & NotKnown<T>): string;
function doSomethingWithString(arg1: KnownString | AnyString, onlyRequiredForKnownString?: boolean): string {
    return "The second argument is only required when `arg1` is a KnownString";
}

const thisIsAKnownString = "knownstring" as KnownString;

// @ts-expect-error Since TS knows that arg1 is a KnownString, it should require the second parameter:
doSomethingWithString(thisIsAKnownString);

Playground link.

【讨论】:

    猜你喜欢
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2020-04-10
    • 2020-06-01
    • 2017-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多