卧槽了 TypeScript 真不是一般人能完全驾驭的,太微妙了,这里的写法搞不懂有什么不对的

2021-10-17 12:44:48 +08:00
 makelove

写了一个复杂的函数定义,怎么搞里面的类型都是 unknown,我把问题部分最简化成这样:

Playground 链接

declare function create<A>(def: {
  a: () => A
  b: (a: A) => void
}): A

let s = create({
    a: () => { return { result: () => 'ok' } },
    b: (a) => {},
})

let s2 = create({
    a: () => { return { result() { return 'ok' } } },
    b: (a) => {},
})

你看这个 s 和 s2 定义是几乎一样的吧,一个出来是正常的类型 { result: () => "ok"; } 另一个是unknown。 谁知道这里到底有什么微妙的东西的里面?

js 怎么搞就这么点东西没有不明明白白的,ts 有时候真抓狂。

6391 次点击
所在节点    TypeScript
46 条回复
makelove
2021-10-17 15:15:35 +08:00
@xarthur 第二个例子,把参数从对象形式写成位置参数就可以正确得到 number,救命啊,这是为啥,把我彻底整不会了

declare function create<S, M, A>(
data: S,
a: (data: S) => M,
b: (m: M) => void
): M

const store2 = create(0, (data) => 1, (m) => {})
noe132
2021-10-17 15:25:11 +08:00
我的直觉是和 contravariance inference 有关,导致 b 覆盖了 a 的类型推导。不过我也不确定是不是一个 bug 。

declare function create<
Data,
A extends (d: Data) => unknown,
M = ReturnType<A>,
>(def: {
data: Data
a: A
b: (m: M) => void
}): [M, ReturnType<A>]


const s = create({
data: 0,
a: (data) => 100,
b: (m) => {},
})

declare function create2<
Data,
A extends (d: Data) => unknown,
M = ReturnType<A>,
>(
data: Data,
a: A,
b: (m: M) => void,
): [M, ReturnType<A>]


const s2 = create2(
0,
(data) => 100,
(m) => {},
)

这里在 create 中,M 和 ReturnType<A> 其实是 1 个 Type,但是 M 被用到 b 的参数后,就变成了 unknown
而在 create2 中,所有的参数都是一样的,只是把对象拆成了 3 个参数,此时 M 的类型被正确推导成了 number

https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzDigxAB4AoeeAEWKgBpL4BBeEADxNWAGd4AKYAC4adAJTwAvAD54aANaocAd1SMqAWSnwASiAzIYqACoBPAA5lm0xtMEhEIgN5NgdEbQxQmUEcyYARiL8ALYiGhIy8ABuOFjA5AC+YiIA2hr0uvqGJhZW0gC65MVgeDwY8HySBEQk-C5Ubl4iAAzq8L4CTVCRsgCMLW2BwSG98E6JjMnFoJCwCCjo2Hg1IMQgAEwUVJ4MTKwcXLxdHuJSsgpKqu1a1XoGRmaWpNa2-K7uol7tncztQQIwvAIucYnFgIwUvB0pl7jknvkiiUyhUeBttIQ1iQNu8qEMqIIzlEBvjAWMJpDyEA
FrankFang128
2021-10-17 15:44:13 +08:00
因为 () => 'ok' 没有 this,所以类型是明确的;
但 () { return 'ok' } 有 this,你没有指定 this 的类型,所以类型是不明确的;
TS 的推测规则是,只要有任何一点不明确,就不推测。
所以你需改成 return { result(this:any) { return 'ok' } } 就行了
FrankFang128
2021-10-17 15:44:44 +08:00
FrankFang128
2021-10-17 15:45:34 +08:00
所以说 JS 的 this 是真的难
makelove
2021-10-17 15:48:44 +08:00
@noe132 确实离谱

declare function create<S, M, A>(def: { data: S, a: (data: S) => M, b: (m: M) => void }): M
const s = create({ data: 0, a: (data) => 100, b: (m) => {}})

declare function create2<M, A>(def: { a: () => M, b: (m: M) => void }): M
const s2 = create2({ a: () => 100, b: (m) => {}})

这里 s 是 unknown, s2 是 number,不知道 data 影响了啥
makelove
2021-10-17 15:51:57 +08:00
@FrankFang128 你看后面的例子用数字了没有 this,我不知道这二个是不是同一个问题
FrankFang128
2021-10-17 15:53:27 +08:00
@makelove 原理看 23 楼:TS 的推测规则是,只要有任何一点不明确,就不推测。
你需要给 data 一个类型,就算是 any 都行
FrankFang128
2021-10-17 15:54:27 +08:00
总之就一句话:TS 的推测规则是,只要有任何一点不明确,就不推测。
cxk0
2021-10-17 19:46:39 +08:00
所以 V2 都是前端大佬,我 Java 后端在这里格格不入
leafre
2021-10-17 20:35:15 +08:00
s1 s2 返回类型 不一样
zbinlin
2021-10-18 00:41:18 +08:00
你这里 a 返回值跟 b 里的参数都需要推断,如果没有在 a 返回值或 b 的参数中推断出一个明确的类型,就无法确定类型。
你可以在 a 里定义下 this 或者将 b 里的 a 参数明确定义成出来(可以是 any 或 unknown )。

同理,你在楼层里举的例子也是一样的问题。
coolmenu
2021-10-18 07:12:10 +08:00
看的脑仁疼,ts 这么复杂吗?
sunwang
2021-10-18 09:27:45 +08:00
不知道楼主的 ts 是什么版本,我的 ts 是可以推断的,版本是 3.7.2
lscho
2021-10-18 10:09:45 +08:00
这明显 s 和 s2 不一样啊,楼主为什么会得出 s 和 s2 定义是几乎一样。匿名函数没有 this 。
makelove
2021-10-18 11:14:34 +08:00
@lscho 那你解释一下为什么 this 会影响这个推断,以及下面的例子用数字也不能推断。
而把参数从对象形式改成位置形式就完全可以推断了


@sunwang playground 网页上可以选版本,3.7 也不行
makelove
2021-10-18 11:16:08 +08:00
@zbinlin 你是说 ts 二个类型不能同时推?那为什么改成位置参数就可以同时推呢?
SmiteChow
2021-10-18 11:30:10 +08:00
讲句实话,写 ts 大部分人遇到复杂对象类型注解都写 any,实践里也没毛病。
thtznet
2021-10-18 11:43:56 +08:00
我不写 TS,只会写 JS 和 C#,传问 TS 是像 C#一样的 JS,但是看了这个问题,我表示 TS 在正宗的强类型(C#)前还是像个玩具语言,我本来打算学一下 TS 让 JS 写得舒服一点,现在有点慌,到底要不要把 JS 过渡到 TS ?既不像 C#那么严谨也丢失了 JS 的灵活和随性,这个搞得有点尴尬。
lscho
2021-10-18 11:51:23 +08:00
@makelove 我理解的和 23 楼一样。ts 是可以从上下文环境中推断类型的。() => 'ok' 这样本身没有 this,所以从外层上下文环境中可以推断。() { return 'ok' }这样单独生成了作用域,有了 this,且没有指定,所以类型不明确。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/808330

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX