我正在尝试生成一个通用函数,它允许我为给定类型生成一个带有回调的强类型 setter - 例如:
interface Foo {
a: number,
b: string
}
magick('a', 43) => {} // Should work
magick('a', '43') => {} // Should fail
我已经实现了执行此操作的通用函数 - 并且它有效。但是,如果我尝试复制该函数,则不会强制执行类型安全(或者更可能是我误解了 typescript !)
interface Test {
a: number;
b: string;
}
interface Test2 {
a2: boolean;
b2: '+' | '-';
}
const testVal: Test = {
a: 42,
b: 'test',
};
type Demo<T> = <K extends keyof T> (key: K, val: T[K]) => void
const implDemo: Demo<Test> = (key, val) => {
testVal[key] = val;
};
首先 - 该函数按照我想要的方式工作:
/* prints: {a: 10, b: "test"} - correct */
implDemo('a', 10); console.log(testVal);
/* Fails as expected - type safety - a should be number */
implDemo('a', 'text');
但是为什么下面的可能呢?怎么可以Demo<Test2>
可分配给 Demo<Test>
/* Create a pointer to implDemo - but with WRONG type!!! */
const implDemo2: Demo<Test2> = implDemo;
implDemo2('a2', true);
console.log(testVal);
/* prints: {a: 10, b: "test", a2: true} - we just violated out type!!!! */
我们刚才在上面做的是一样的:
testVal['a2'] = true; /* Doesn't work - which it shouldn't! */
这是另一种简单类型,实际上强制执行类型安全
type Demo2<T> = (val: T) => void;
const another: Demo2<string> = (val) => {};
/* This fails - as expected */
const another2: Demo2<number> = another;
这是 typescript 中的错误 - 还是我误解了什么?我怀疑是类型 Demo<T> = <K extends keyof T>
是罪魁祸首,但我根本不明白我是如何被允许以这种方式“破解”类型系统的。
最佳答案
我会说这是一个编译器错误。 我不确定是不是reported然而;到目前为止我还没有通过搜索找到任何东西,但这并不意味着它不存在。 编辑:我已经提交了 an issue关于这种行为;我们将看看会发生什么。更新:看起来这可能是 fixed对于 TS3.5 TS3.6 TS3.7...谁知道呢?!
这是一个复制品:
interface A { a: number; }
interface B { b: string; }
type Demo<T> = <K extends keyof T> (key: K, val: T[K]) => void
// Demo<A> should not be assignable to Demo<B>, but it is?!
declare const implDemoA: Demo<A>;
const implDemoB: Demo<B> = implDemoA; // no error!? 😕
// Note that we can manually make a concrete type DemoA and DemoB:
type DemoA = <K extends keyof A>(key: K, val: A[K]) => void;
type DemoB = <K extends keyof B>(key: K, val: B[K]) => void;
type MutuallyAssignable<T extends U, U extends V, V=T> = true;
// the compiler agrees that DemoA and Demo<A> are mutually assignable
declare const testAWitness: MutuallyAssignable<Demo<A>, DemoA>; // okay
// the compiler agrees that DemoB and Demo<B> are mutually assignable
declare const testBWitness: MutuallyAssignable<Demo<B>, DemoB>; // okay
// And the compiler *correctly* sees that DemoA is not assignable to DemoB
declare const implDemoAConcrete: DemoA;
const implDemoBConcrete: DemoB = implDemoAConcrete; // error as expected
// ~~~~~~~~~~~~~~~~~ <-- Type 'DemoA' is not assignable to type 'DemoB'.
你可以看到DemoA
和 Demo<A>
基本上是相同的类型(它们可以相互分配,这意味着一种类型的值可以分配给另一种类型的变量)。和 DemoB
和 Demo<B>
也是同一类型。并且编译器确实理解 DemoA
不可分配给 DemoB
.
但是编译器认为 Demo<A>
可分配给 Demo<B>
,这是你的问题。就好像编译器“忘记”了什么 T
在 Demo<T>
是。
如果您现在真的需要解决这个问题,您可能需要标记 Demo<T>
用会“记住”什么的东西T
是,但不会阻止您将您的实现分配给它:
// workaround
type Demo<T> = (<K extends keyof T> (key: K, val: T[K]) => void) & {__brand?: T};
const implDemoA: Demo<A> = (key, val) => {} // no error
const implDemoB: Demo<B> = implDemoA; // error here as expected
好的,希望对你有帮助;祝你好运!
关于Typescript 泛型类型不强制类型安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55591663/