【问题标题】:Subsequent variable declarations must have the same type. any后续的变量声明必须具有相同的类型。任何
【发布时间】:2017-07-14 00:03:54
【问题描述】:

我正在使用 ava(没有链接,因为我不允许使用超过 2 个????)进行测试,并且想要输入 ava 的测试上下文。我是typed as any in ava's definition file

我特别想要的是打字稿编译器知道t.context 在以下测试中属于{foo: number} 类型:

import test from 'ava'

test.beforeEach((t) => {
  t.context = { foo: 5 }
})

test('Is context typed', (t) => {
  // uncaught typo
  t.is(t.context.fooo, 5)
})

我尝试使用declaration merging 执行此操作,但使用TS2403: Subsequent variable declarations must have the same type. Variable 'context' must be of type 'any', but here has type '{ foo: number; }'. 失败:

declare module 'ava' {
    interface ContextualTestContext {
      context: {
        foo: number,
      }
    }
}

test.beforeEach((t) => {
  t.context = { foo: 5 }
})

test('Is context typed', (t) => {
  // uncaught ypo
  t.is(t.context.fooo, 5)
})

有没有办法做到这一点,而无需像这样一直投射上下文:

interface IMyContext {
  foo: number
}

test.beforeEach((t) => {
  t.context = { foo: 5 }
})

test('Is context typed', (t) => {
  const context = <IMyContext> t.context
  // caught typo
  t.is(context.fooo, 5)
})

【问题讨论】:

    标签: typescript ava


    【解决方案1】:

    没有通用的方法来做到这一点。在您的特殊情况下,您可以创建一个新的TestContext,例如而不是

    export type ContextualTest = (t: ContextualTestContext) => PromiseLike<void> | Iterator<any> | Observable | void;
    

    使用类似的东西

    export type MyContextualTest<T> = (t : TestContext & {context : T}) => PromiseLike<void> ...
    

    并声明你自己的test函数,它应该像这样兼容Ava:

    interface MyTestFunction<T> {
        (name : string, run : MyContextualTest<T>)
    }
    
    import {test as avaTest} from 'ava';
    const test : MyTestFunction<IMyContext> = avaTest;
    

    这主要是未经测试的,所以如果有一些问题,请告诉我。

    【讨论】:

    • @despairblue 您能否为您的用例提出问题?也许我们可以接受test() 签名中的泛型。
    • @FinnO 在技术上有效,但如果我想使用在test 函数本身上定义的函数(如test.beforeEach),我将不得不重新定义所有声明。其中大约有 1700 个。
    【解决方案2】:

    在 ava 的下一个版本中可以输入上下文。然后你可以这样做:

    import * as ava from 'ava';
    
    function contextualize<T>(getContext: () => T): ava.RegisterContextual<T> {
        ava.test.beforeEach(t => {
            Object.assign(t.context, getContext());
        });
    
        return ava.test;
    }
    
    const test = contextualize(() => {
        return { foo: 'bar' };
    });
    
    test.beforeEach(t => {
        t.context.foo = 123; // error:  Type '123' is not assignable to type 'string'
    });
    
    test.after.always.failing.cb.serial('very long chains are properly typed', t => {
        t.context.fooo = 'a value'; // error: Property 'fooo' does not exist on type '{ foo: string }'
    });
    
    test('an actual test', t => {
        t.deepEqual(t.context.foo.map(c => c), ['b', 'a', 'r']); // error: Property 'map' does not exist on type 'string'
    });
    

    如果您异步获取上下文,则需要相应地更改contextualize 的类型签名:

    function contextualize<T>(getContext: () => Promise<T>): ava.RegisterContextual<T> {
        ava.test.beforeEach(async t => {
            Object.assign(t.context, await getContext());
        });
    
        return ava.test;
    }
    
    const test = contextualize(() => {
        const db = await mongodb.MongoClient.connect('mongodb://localhost:27017')
    
        return { db }
    });
    

    否则 TypeScript 编译器会认为 t.context 是一个 Promise,尽管它不是

    【讨论】:

      猜你喜欢
      • 2017-11-15
      • 2018-04-18
      • 1970-01-01
      • 2016-11-07
      • 2019-09-04
      • 1970-01-01
      • 2016-04-11
      • 2020-09-11
      • 2017-10-23
      相关资源
      最近更新 更多