首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >坏函数分配

坏函数分配
EN

Stack Overflow用户
提问于 2021-04-02 08:54:14
回答 2查看 87关注 0票数 5

在非严格模式下,我遇到了一个问题,下面是我的代码,我可以将一个类型不好的函数分配给一个类型化变量。

我会犯错吗?

谢谢。

代码语言:javascript
复制
interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

interface C {
    f3( ) : void;
}

type expectA = ( a: A ) => void;
type expectB = ( b: B ) => void;


function testA( a: A ) {
    console.log( "a" );
}

function testB( b: B ) {
    console.log( "b" );
}

function testC( c: C ) {
    console.log( "c" );
}


let v1: expectA = testA;    // ok: assign type A to a type A
let v2: expectA = testB;    // ok: assign type B (extending A) to a type A
let v3: expectA = testC;    // fail -> normal: error TS2322: Type '(c: C) => void' is not assignable to type 'expectA'.
let v4: expectB = testA;    // ok -> **abnormal**: there is no error in !strict mode
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-04-02 10:50:46

TypeScript实际上是在保护您避免在这里做一些愚蠢的事情,但它是以一种令人困惑的方式这样做的。

让我们将示例简化为AB接口。

代码语言:javascript
复制
interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

type expectA = ( a: A ) => void;
type expectB = ( b: B ) => void;


function testA( a: A ) {
    a.f1();
}

function testB( b: B ) {
    b.f1();
    b.f2();
}


const v1: expectA = testA;
const v2: expectA = testB; // error: Property 'f2' is missing in type 'A' but required in type 'B'
const v3: expectB = testB;
const v4: expectB = testA;

乍一看,只有v2有错误的底部的结果似乎有违直觉。如果B扩展了A,那么为什么不能在可以使用A的任何地方使用B

答案是因为我们处理的是函数。仔细看看testB()的实现。它调用属性b.f2(),因为它期望其参数b具有该属性。但是const v2: expectB的左手边等同于(a: A) => void类型。A类型的参数没有f2()。因此,我们告诉TypeScript关于什么是正确的v2类型的矛盾的事情;它要么是一个有a: A的函数,在这种情况下,a.f2()是不安全的调用,要么是一个有b: B的函数,在这种情况下是安全的。这是个悖论!

(请记住,这与testB是否真的试图调用b.f2()无关;关键是它可以,根据参数类型的设置方式,这将导致v2场景中的运行时错误。)

现在,对于const v4,您说您认为这样做是“异常的”,但是如果我们再次仔细查看这些函数,我们可以看到,这是合理的。如果将类型AB变量传递给testA(),则不会出现可能的错误,因为它永远不会尝试访问f2()属性。

还请注意,extends在TypeScript中的工作方式并不像您所期望的那样。编写interface B extends A时简单地说,B将继承A的所有属性。它没有建立任何类型的关系,允许B代表A的任何实例。这种行为称为“多态”,要做到这一点,就需要使用类,例如class B extends A implements A

代码语言:javascript
复制
class A {
    foo = '';
}

class B extends A implements A {
    bar = '';
}

let isA = new A();
isA = new B(); // this is fine because B implements A
let isB = new B();
isB = new A(); // error: Property 'bar' is missing in type 'A' but required in type 'B'
票数 1
EN

Stack Overflow用户

发布于 2021-04-06 08:03:32

谢谢您的回答,但实际上我的问题是关于严格/vs非严格模式的。(我在最初的描述中犯了一个小错误,对不起)

当我说非严格模式时,我的意思是在tsconfig.json中没有这个选项

代码语言:javascript
复制
"strict": true, /* Enable all strict type-checking options. */

使用您的简化版本:

代码语言:javascript
复制
interface A {
    f1( ) : void;
}

interface B extends A {
    f2( ) : void;
}

type expectA = ( a: A ) => void;
  
function testB( b: B ) {
    console.log( "b" );
    b.f1();
    b.f2();
}
    
// here in strict mode there is an error and that's ok 
// but in non strict mode there is no error
let v2: expectA = testB;    // error TS2322: Type '(b: B) => void' is not assignable to type 'expectA'.

class ClassB implements B {
    f1( ) {}
    f2( ) {}
}

let t = new ClassB( );
v2( t );

我不明白为什么这条线被允许在非严格的模式下。仅仅因为B接口和A接口之间有关系,就允许这样做。如果删除此继承,则编译器将发出抱怨。

代码语言:javascript
复制
let v2: expectA = testB
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66916829

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档