也许我正在失去它。
我有可能的核苷酸列表和相应的类型
:
const DNA = ['G', 'C', 'T', 'A'] as const;
type DNA = typeof DNA[number];
现在,DNA 链
可以是任意字符 (GCTA) 的任意组合、任意长度的字符串。这里重要的是,每个单独的角色都是 DNA
类型。
如果我split('')
这个strand
,我想得到一个字符数组,每个字符的类型为DNA
,例如,
const strand: ??? = "GACATAGACGCGTTAG";
const DNAstring: DNA[] = strand.split('');
在这种情况下如何输入strand
? 🤯
如果我将其输入为字符串
,TypeScript 会感到不安,这是可以理解的:类型 'string[]' 不能分配给类型 '("G"| "C"| "T"| "A")[]'
.
非常感谢任何帮助!
最佳答案
不幸的是,TypeScript 中没有特定类型来表示仅由某个集合中的字符组成的字符串。有一个长期建议支持regular expression已验证的字符串类型;请参阅microsoft/TypeScript#41160关于该主题当前 Unresolved 问题。如果存在这样的类型,你可能会写
// NOT VALID TS, don't do this:
type DNAStrand = /[GCTA]*/;
然后就完成了。唉,这是不可能的(还?)。如果您想(稍微)增加将来实现此功能的机会,您可以转到该问题,给它一个👍,并描述您的用例,为什么它引人注目(有人告诉我 DNA 链不是主要的大多数人的用例),以及为什么当前的解决方案(我在下面介绍一些)还不够。
目前,TypeScript 有 template literal types ,它允许您对字符串 literal types 执行一些按字符操作。 。人们可能会天真地尝试表示 DNAStrand
作为union type空字符串加上一个 DNA
接下来是另一个 DNAStand
...有点像BNF语法表示:
// NOT VALID TS, don't do this:
type DNAStrand = "" | `${DNA}${DNAStrand}` // can't do this
唉,这被视为无效的循环;联合体是急切计算的,编译器会在某个时候崩溃,所以它会提示。
您可以使用 recursive conditional type 将所有有效的 DNA Strand 字符串文字联合起来,达到一定的长度。 :
type RepeatLessThan<N extends number, T extends string, A extends string[] = [""]> =
N extends A['length'] ? A[number] : RepeatLessThan<N, T, [`${T}${A[0]}`, ...A]>
// and this works for short lengths
type DNAStandUpToThree = RepeatLessThan<4, DNA>;
/* type DNAStandUpToThree = "" | "G" | "C" | "T" | "A" | "GG" | "GC" | "GT" | "GA" | "CG" | "CC" | "CT" |
"CA" | "TG" | "TC" | "TT" | "TA" | "AG" | "AC" | "AT" | "AA" | "GGG" | "GGC" | "GGT" | "GGA" | "GCG" | "GCC"
| "GCT" | "GCA" | "GTG" | "GTC" | "GTT" | "GTA" | "GAG" | "GAC" | "GAT" | "GAA" | "CGG" | "CGC" | "CGT" |
"CGA" | "CCG" | "CCC" | "CCT" | "CCA" | "CTG" | "CTC" | "CTT" | "CTA" | "CAG" | "CAC" | "CAT" | "CAA" |
"TGG" | "TGC" | "TGT" | "TGA" | "TCG" | "TCC" | "TCT" | "TCA" | "TTG" | "TTC" | "TTT" | "TTA" | "TAG" |
"TAC" | "TAT" | "TAA" | "AGG" | "AGC" | "AGT" | "AGA" | "ACG" | "ACC" | "ACT" | "ACA" | "ATG" | "ATC" |
"ATT" | "ATA" | "AAG" | "AAC" | "AAT" | "AAA" */
但是 TypeScript 只能表示最多包含 100,000 个成员的联合体。您的示例字符串有 16 个字符长,因此您至少需要 4^16 ≈ 40 亿个成员来表示它。没有有用的方法来枚举这些。如果你尝试,你会遇到问题:
// VALID TS, but STILL DON'T DO THIS:
type DNAStandUpToSixteen = RepeatLessThan<17, DNA> // 🔥💻🔥, eventually error with
// "Expression produces a union type that is too complex to represent"
相反,我们可以使用模板文字类型得到的最接近的结果是 DNAStrand
一个generic输入验证或检查字符串文字以查看其是否有效。这使我们不必枚举每个可能的有效链,但它的缺点是处理这些类型的任何内容本身都需要是通用的:
type VerifyDNAStrand<T extends string, A extends string = ""> =
T extends `${infer F}${infer R}` ?
F extends DNA ? VerifyDNAStrand<R, `${A}${F}`> : `${A}${DNA}` :
A
你不能轻易地写const x: DNAStrand = "ACT"
,您需要const x: DNAStrand<"ACT"> = "ACT"
。因此,使用通用辅助标识函数比注释更容易:
const dnaStrand = <T extends string>(
x: T extends VerifyDNAStrand<T> ? T : VerifyDNAStrand<T>) => x;
const goodStrand = dnaStrand("GATTACA"); // okay
const badStrand = dnaStrand("ATTACK OF THE CLONES"); // error
// -----------------------> ~~~~~~~~~~~~~~~~~~~~~~
// Argument of type '"ATTACK OF THE CLONES"' is not assignable to parameter of type
// '"ATTACA" | "ATTACG" | "ATTACC" | "ATTACT"'
那太好了。但是编译器的definition for String.prototype.split()
就像
interface String {
split(separator: string | RegExp, limit?: number): string[];
}
始终输出 string[]
无论。编译器不知道"ABC".split("")
产生["A", "B", "C"]
。所以以上所有内容都适用于 DNAStrand
不能解决您的根本问题:
const stillNotDNAstring: DNA[] = goodStrand.split(''); // error!
所以你又需要 type assertion某处:
const dnaString = goodStrand.split('') as DNA[]; // okay
但这仅仅意味着在类型安全方面您已经从编译器手中接管了。没有什么可以阻止你做错事:
const oops = badStrand.split('') as DNA[]; // still okay
也许您可以将拆分代码包装到一个函数中,其中函数内部的类型安全需要手动处理,但人们至少可以安全地调用它,有点:
function splitStrand<T extends string>(
x: T extends VerifyDNAStrand<T> ? T : VerifyDNAStrand<T>) {
return x.split('') as DNA[];
}
const okay = splitStrand("GATTACA"); // okay
const bad = splitStrand("ATTACK OF THE CLONES"); // compiler error
因此,当涉及到 DNA 链字符串的编译时验证时,这是我能想到的最接近的结果。如果您不需要编译时验证,而只是需要某种方式来跟踪类型,则可以使用带有品牌类型或其他结构的运行时验证,如 the other answer详细信息。
关于TypeScript:<type> 的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72719835/