考虑以下类型
interface ISmth<T> {
id: number;
data: T;
}
现在我要声明一个包含此类项目的数组
var a = [{
id: 1,
data: [],
}, {
id: 2,
data: 4,
}, {
id: 3,
data: "abc",
}] as const
我想使用as const
,因为此数组将永远不会更改,并且它包含我想区分的data
属性的不同类型。
但是同时我希望打字稿确保每个数组元素都由ISmth<any>
表示。我该如何表达这种确认?
答案 0 :(得分:1)
interface ISmth<T> {
id: number;
data: T;
}
var a = [{
id: 1,
data: [],
}, {
id: 2,
data: 4,
}, {
id: 3,
data: "abc",
}] as const
type T = (typeof a extends Readonly<ISmth<any>[]> ? Object : never);
interface X extends T { }
因此,如果满足条件,T
代表Object
并进行代码编译。
否则T
为never
,接口无法扩展never
并产生错误。
答案 1 :(得分:1)
在a
上强制类型约束的两种方法:
仅编译时键入断言
这是@Qwertiy提出的解决方案,它在失败的类型断言情况下使用扩展never
类型的接口来强制执行编译错误。另外,还可以借助generic type constraint触发编译错误。如果我们允许断言类型为通用类型并接收任意多个类型,则可以这样写(Playground):
type AssertAssignable<T, U extends T> = true
var a = [{
id: 1,
data: [],
}] as const
var aError = [{
id: "foo",
data: [],
}] as const
type Assert = AssertAssignable<readonly ISmth<any>[], typeof a>
type AssertError = AssertAssignable<readonly ISmth<any>[], typeof aError> // error(OK!)
涉及辅助函数的类型断言(出于完整性考虑,已从here移出)
可以定义一个辅助函数createA
,其唯一目的是在a
的已创建对象文字上声明类型,同时仍通过as const
({{3 }}):
function createA<A extends Readonly<ISmth<any>[]>>(a: A): A {
return a
}
var a = createA([{
id: 1,
data: []
}] as const);
var aError = createA([{
id: "1", // error (OK!)
data: []
}] as const);
如果createA
未被重用,您甚至可以使用Playground内联声明:
var aInline = (<A extends Readonly<ISmth<any>[]>>(a: A) => a)([{
id: 1,
data: [],
}] as const)
...或定义通用的可导出实用程序帮助器。
function enforceTypedLiteral<T>() {
return <A extends T>(a: A) => a
}
enforceTypedLiteral<readonly ISmth<any>[]
>()([{
id: 1,
data: []
}] as const)