在限制变量类型时保持推断类型

时间:2018-04-27 16:43:39

标签: typescript

foo变量应保留{ a: { b: string } }推断类型,同时限制其他类型Foo。它最好应该有类型{ a: { b: string } } & Foo

type Foo = { [k: string]: { [k: string]: string } };

const _foo = { a: { b: 'c' } };
export const foo: typeof _foo & Foo = _foo;

但它可以接受{ a: { b: string } }类型 - 只要它不符合Foo就会产生类型错误:

type Foo = { [k: string]: { [k: string]: string } };

function checkFoo(foo: Foo) {};

let foo = { a: { b: 'c' } };
checkFoo(foo);

目标是使TypeScript仅发出单个JavaScript行:

var foo = { a: { b: 'c' } }; 

checkFoo函数也可以出现在编译器输出中,只要它没有被调用,所以它可以被minifier删除为死代码。

如果可能的话,我更愿意避免像checkFoo(foo)这样的不必要的编译器输出。

这里有什么选择?是否有特定于最新TypeScript版本2.7和2.8的那些?

3 个答案:

答案 0 :(得分:3)

虽然我不一定同意不惜一切代价避免额外函数调用的前提,但如果foo未实现预期的接口而不使用,我们可以生成不发出JS代码的编译器错误功能。

该方法基于创建条件类型(2.8中提供),该类型根据常量的类型是否实现接口返回不同的字符串文字类型。然后我们可以在我们期望特定类型文字的地方使用此类型。

要获得错误,我们需要以不产生代码的方式使用该类型,并且我在声明或类型别名中找到了两种可能的方法。

export type Foo = { [k: string]: { [k: string]: string } };

type TestType<T, TBase> = T extends TBase ? "OK" : "Implementation does not extend expected type";
type Validate<T extends "OK"> = "OK";

let foo = { a: { b: 'c' } };
// use type in a constant 
declare const fooTest : Validate<TestType<typeof foo, Foo>> // ok
// use type in a type alias
type fooTest = Validate<TestType<typeof foo, Foo>> //ok

let fooBad = { a: { b: 10 } };
declare const fooBadTest : Validate<TestType<typeof fooBad, Foo>>;  // error: Type '"Implementation does not extend expected type"' does not satisfy the constraint '"OK"'

type fooBadTest = Validate<TestType<typeof fooBad, Foo>> // error: Type '"Implementation does not extend expected type"' does not satisfy the constraint '"OK"'.

这种方法的一个问题是我们必须引入污染名称空间的额外类型别名/常量名称。

游乐场link

答案 1 :(得分:2)

你可以写一个函数:

function makeFoo<T extends Foo>(x: T): T & Foo {
    return x as T & Foo;
}
// OK, foo: what you want
export const foo = makeFoo({ a: { b: 'c' } });
// Error
export const bar = makeFoo({ a: { b: 12 } });

答案 2 :(得分:1)

使用与this answer中描述的方法类似的方法可以避免不必要的输出:

type Foo = { [k: string]: { [k: string]: string } };
type CheckFoo<T extends Foo> = T;

let foo = { a: { b: 'c' } };
type _checkFoo = CheckFoo<typeof foo>; // produces an error if foo isn't compatible