简体   繁体   English

Typescript泛型类型不强制类型安全

[英]Typescript generic type does not enforce type safety

I'm trying to generate a generic function that allow me to generate a strongly typed setter for a given type, with a callback - eg: 我正在尝试生成一个通用函数,允许我为一个给定类型生成一个强类型的setter,带有一个回调 - 例如:

interface Foo {
   a: number,
   b: string
}

magick('a', 43) => {} // Should work
magick('a', '43') => {} // Should fail

I've implemented a generic function that does this - and it works. 我已经实现了一个通用函数来实现这一点 - 它的工作原理。 But however if I try to copy that function type safety is not enforced (or more likely I'm misunderstanding 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;
};

Firstly - the function is working as how I want it: 首先 - 该功能正如我所希望的那样:

/* prints: {a: 10, b: "test"} - correct  */
implDemo('a', 10); console.log(testVal); 

/* Fails as expected - type safety - a should be number */
implDemo('a', 'text'); 

But why is this following possible? 但为什么这样可能呢? How Can Demo<Test2> be assignable to Demo<Test> 如何将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!!!! */

What we just did above is the same as doing: 我们上面刚刚做的是和做的一样:

testVal['a2'] = true; /* Doesn't work - which it shouldn't! */

Here is another simply type, where type safety is actually enforced 这是另一种简单类型,其中实际强制执行类型安全

type Demo2<T> = (val: T) => void;
const another: Demo2<string> = (val) => {};
/* This fails - as expected */
const another2: Demo2<number> = another;

Is this a bug in typescript - or am I misunderstanding something? 这是打字稿中的错误 - 还是我误解了什么? My suspicion is that type Demo<T> = <K extends keyof T> is the culprit, but I simply don't understand how I'm allowed to "hack" the typesystem this way. 我怀疑类型Demo<T> = <K extends keyof T>是罪魁祸首,但我根本不明白我是如何允许以这种方式“破解”类型系统的。

I'd say this is a compiler bug. 我想这是一个编译器错误。 I'm not sure if it has been reported yet; 我不确定它是否已被 报道 ; so far I haven't found anything by searching, but that doesn't mean it isn't there. 到目前为止,我没有通过搜索找到任何东西,但这并不意味着它不存在。 EDIT: I've filed an issue about this behavior; 编辑:我已经提出有关此行为的问题 ; we'll see what happens. 我们会看到会发生什么。 UPDATE: looks like this might be fixed for TS3.5 TS3.6! 更新:看起来像这可能是固定 TS3.5 TS3.6!

Here's a reproduction: 这是一个复制品:

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'.

🔗Link to code👨‍💻 🔗链接到代码👨💻

You can see that DemoA and Demo<A> are basically the same type (they are mutually assignable, meaning a value of one type can be assigned to a variable of the other). 您可以看到DemoADemo<A>基本上是相同的类型(它们是可相互分配的,这意味着可以将一种类型的值分配给另一种类型的变量)。 And DemoB and Demo<B> are also the same type. DemoBDemo<B>也是同一类型。 And the compiler does understand that DemoA is not assignable to DemoB . 并且编译器确实理解DemoA不能分配给DemoB

But the compiler thinks that Demo<A> is assignable to Demo<B> , which is your problem. 但是编译器认为Demo<A>可以分配给Demo<B> ,这是你的问题。 It's as if the compiler "forgets" what T in Demo<T> is. 这是因为如果编译器“忘记”什么TDemo<T>是。


If you really need to work around this for now you might want to brand Demo<T> with something that will "remember" what T is, but doesn't stop you from assigning your implementation to it: 如果你现在真的需要解决这个问题,你可能想要用“记住” T是什么来标记Demo<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

Okay, hope that helps; 好的,希望有所帮助; good luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 TypeScript泛型的类型安全 - Type safety of TypeScript Generic 强制使用泛型类型的值的类型安全 - Enforce type safety of a value of a generic type TypeScript:在函数中强制使用通用推断的类型参数 - TypeScript: Enforce generic inferred type parameter in function Typescript 是否允许破坏类型安全的突变? - Does Typescript allow mutations that break type safety? 为什么 typescript 不能确保在 React 中添加额外属性的通用高阶组件中的类型安全? - Why typescript does not ensure type safety in a generic higher order component that adds additional properties in React? 强制执行可选类型打字稿 - Enforce optional type typescript 如何使用 TypeScript 泛型函数强制传入类型上存在的属性? - How to use a TypeScript generic function to enforce a property existing on a type passed in? TypeScript:在嵌套的静态类中强制执行泛型类型约束 - TypeScript: Enforce generic type constraint in a nested static class 是否可以在没有声明的超类泛型的情况下强制泛型抽象方法的类型安全? - Is it possible to enforce type-safety of generic abstract methods without declared superclass generics? Typescript 异步函数泛型; 如何强制使用通用返回类型的通用异步函数? - Typescript async function generic; How to enforce async function generic with generic return type?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM