由于存在类型定义时会忽略const

时间:2019-07-17 06:32:43

标签: typescript typescript-typings

我想将一个对象限制为某种类型。但是我也希望能够使用“ as const”强制转换,以便可以将某些属性键入为类型文字。

请考虑以下对象:

user$ adb shell setprop persist.sys.timezone $"GMT-11.00"

我希望所有过滤器都遵循setprop: failed to set property 'persist.sys.timezone' to 'GMT-11.00' 接口的约束。但我希望打字稿将const COST_FILTER: IFilterBase = { type: "cost", displayName: "Cost", fields: costFilterFields, } as const 的类型视为IFilterBase。可能吗?

2 个答案:

答案 0 :(得分:4)

如果您明确指定类型,则打字稿将仅检查与接口的兼容性。评论中概述了一项建议,以支持该语言。

在这种情况发生之前,我们可以使用函数,元组和文字的推理规则:

  • 如果将文字类型分配到一个位置,该位置被键入为一个通用类型参数,并且该类型被限制为可以具有文字的类型,则将推断出文字类型(或最好保留)。
  • 如果将数组分配给作为约束为[unknown] | unknown[]的通用类型参数键入的位置,则将推断出元组。

使用这些规则,我们可以创建一个递归映射类型,以将原始类型的属性映射到包含此类通用类型参数的新类型。我们没有为每个属性都分隔类型参数,一个参数将用于文字,一个参数将用于元组。这足以向编译器提示我们想要的东西。


type WithLiterals<T, L, LTuple> =  
    T extends string| number | boolean | null | undefined ? T & L :
    {
        [P in keyof T]: 
            WithLiterals<T[P], L, LTuple> & (T[P] extends Array<any> ? LTuple: unknown)
    }

type DeepReadonly<T> = {
    readonly [P in keyof T]: DeepReadonly<T[P]>
}

function asConst<TInterface>()
{
    return function<
        LTuple extends [unknown] | unknown[],
        L extends string | boolean | number, T extends WithLiterals<TInterface, L, LTuple>>(o: T): DeepReadonly<T> {
        return o as any
    }
}

type IFilterBase = {
    type: "cost" | "other",
    displayName: string | undefined,
    nr: number,
    nrUnion: 1 | 2,
    subObj : {
        a: string;
    }
    arr: string[]
    larr: ("A" | "B")[]
    mixedarr: (number | string)[],
    oArray: Array<{
        a: string
    }>
}

export const COST_FILTER = asConst<IFilterBase>()({
   type: "other",
   nr: 1,
   nrUnion: 1,
   displayName: "Cost",
   subObj: {
       a: "A"
   },
   arr: ["A", "B"],
   larr: ["A"],
   mixedarr: [1, ""],
   oArray: [
       { a: ""}
   ]
})

键入为:

export const COST_FILTER : DeepReadonly<{
    type: "other";
    nr: 1;
    nrUnion: 1;
    displayName: "Cost";
    subObj: {
        a: "A";
    };
    arr: ["A", "B"];
    larr: ["A"];
    mixedarr: [1, ""];
    oArray: [{
        a: "";
    }];
}>

Link

答案 1 :(得分:0)

这可以通过使用虚拟验证功能来实现。

const validateType = <T> (obj:T) => undefined 

剩下的就是使用类型和对象来调用它:

const COST_FILTER: IFilterBase = {
   type: "cost",
   displayName: "Cost",
   fields: costFilterFields,
} as const

validateType<FilterBase>(COST_FILTER) // Will show an error if types don't match.