使用TypeScript定义带有条件属性/限制的类型

5

我刚接触typescript,学会了如何定义自定义类型,例如:

type T = {a:number, b:any}

是否可以使用类中定义的类型构造函数,在TypeScript中定义所有长度大于2的字符串集合的类型?

或者,也许可以为所有大于0的数字定义一种类型?


不,你只能使用“any”或其他数据类型来声明变量,没有任何逻辑限制。 - Nothing Mi
1个回答

6
虽然你不能在编译时强制执行这种任意限制,但是你可以创建类型,强制用户调用执行这些验证的函数,然后使用品牌类型在代码中依赖这些不变量。
type PositiveNumber =  number & { positive: true}
type StringOfMinLength<T extends number> =  string & { minLegth: T}

type T = {a:PositiveNumber, b:StringOfMinLength<3> }

function isPositiveNumber(value: number): value is PositiveNumber {
    if( value < 0 ) return false
    return  true;
}
function asPositiveNumber(value: number) {
    if( !isPositiveNumber(value) ) throw new Error("Not ok")
    return value; // type guard above, value will now be a PositiveNumber 
}

function isStringOfMinLength<T extends number>(value: string, length: T): value is StringOfMinLength<T> {
    if( value.length < length ) return false;
    return true;
}

function asStringOfMinLength<T extends number>(value: string, length: T): StringOfMinLength<T> {
    if(!isStringOfMinLength(value, length) ) throw new Error("Not ok")
    return value; // type guard above, value will now be a PositiveNumber 
}

type MyData = {a:PositiveNumber, b:StringOfMinLength<3>}
let myObj: MyData = {
    a: asPositiveNumber(0),
    b: asStringOfMinLength("Test", 3),
}

Math.sqrt(myObj.a) // a will be greater then 0
myObj.b[2] // index will exist, length greater then 3

let myNotOkObject: MyData = {
    a: -1, // will be a compile error, the checking function is not called
    b: "Test" // this will also be an error event though it satisfies the constraint since we don't call the appropriate function
}

// we can also use the type guard version instead (is*) of the assertion version (as*)
let a = 10;
let b = "Test"
if(isPositiveNumber(a) && isStringOfMinLength(b, 3))
{
    let myOtherObj: MyData = { a, b } // a and b are PositiveNumber and respectively StringOfMinLength<3>
} else {
    // handle case when they are not what was expected
}

您可以在任何需要基础类型的地方使用品牌类型(例如Math.sqrt(myObj.a)),但是您不能直接将基础类型分配给品牌类型的字段。这是否在实际代码中有价值取决于您和您的用例。

这篇文章对品牌类型进行了更多讨论。

编辑

添加了创建带有类型保护版本的品牌类型函数,这样您就可以检查不变量是否为真,并自行处理假情况,而不是引发错误。感谢@AluanHaddad提供的想法。


为什么不使用类型保护而不是抛出异常呢?这样你就不必使用类型断言,也不必分配结果。当然,您的方法完全有效。 - Aluan Haddad
1
@AluanHaddad 我当时没有考虑到类型守卫,但你是对的,函数的类型守卫版本会非常有用。但两个版本都是需要的,抛出异常的版本和类型守卫版本,在某些情况下,仅仅断言值是有效的并在否则抛出错误会更容易,而在其他情况下,使用守卫会更好。我很快就会添加一个类型守卫版本。感谢你的建议 :-) - Titian Cernicova-Dragomir
好主意。既然你提到了,你可以从抛出异常的验证器中调用类型保护函数。这应该可以从类型保护中推断出验证器的结果类型,因为 false 分支会抛出异常。这样可以实现一些不错的代码重用 :) 无论如何 +1 - Aluan Haddad
1
@AluanHaddad 是的,那就是我要做的方式,这样验证器版本看起来更好,没有断言 :) 再次感谢您。 - Titian Cernicova-Dragomir
在你的示例中,哪里有"强制用户调用执行这些验证的函数的类型"? - Melab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接