写了一个复杂的函数定义,怎么搞里面的类型都是 unknown,我把问题部分最简化成这样:
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 有时候真抓狂。
1
akinoniku 2021-10-17 12:54:26 +08:00
s 和 s2 在 js 不是等价的
|
2
makelove OP @akinoniku 是,就差了个 this 处理。
我这里需要传入一个函数集,所以我这里只能写个注解让别人传入函数集时要用 => 写法不能用另一种?这有点太搞笑了 |
3
codehz 2021-10-17 13:12:35 +08:00
s2 改成这样
let s2 = create({ a: () => { return { result() { return 'ok' } } as const }, b: (a) => {}, }) |
5
hronro 2021-10-17 13:18:47 +08:00
感觉有点像是 TypeScript 本身的 BUG,建议你去 TypeScript 报个 issue
|
6
makelove OP @hronro 这就不给别人添乱了,这个不可能会是未知 BUG,这么常用的东西即使是 BUG 也有大把人在提了,毕竟 ts 这流行度摆在那
|
7
gynantim 2021-10-17 13:47:13 +08:00
不是 bug 吧。因为 s 能够通过静态分析得到类型。s2 不能。
|
8
hronro 2021-10-17 13:58:56 +08:00
@makelove #6
我好心帮你看问题,为什么说我添堵? 如果一个 Object literal 直接申明能推断出类型,inline 到某个对象里就不能推断出类型,这为什么不可能是 TS 的 BUG? |
9
makelove OP @gynantim 请问:
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) => {} }) 这种情况,为何 s 也是 unknown 呢 感觉这是 ts 的某种特点,不过不知道在哪能有说明 |
10
makelove OP @hronro 怎么说呢,我不是说这不一定不是个 bug,而是说即使是 bug typescript 的 issue 列表里也肯定有了。(以前我初学 ts 时就提过几个百思不得其解的 bug,后来发现都是别人提烂的,typescript 几万个 issue 不是盖的,你能想到的几乎都有,我想不是资深 ts 人士提不出新 bug
包括这个,因为不好找关键词所以我没找到相关 bug |
11
xarthur 2021-10-17 14:13:42 +08:00 1
首先 S1 和 S2 **应该**返回的类型是不一样的。
S1 返回的是 { result: () => string; },是个对象,里面有个 result 的属性,这个属性的类型是 () => string S2 返回的是 { result() : string },是个对象,这个对象里有个 result() 的方法,这个方法的返回值应该是 string 但是在你的写法中你是没法判断{ return { result() { return 'ok' } } }这句话是要返回一个对象还是一个 闭包 /代码块 的,类型推断只能出 unknown 。 我是这么理解的。 |
12
xarthur 2021-10-17 14:17:28 +08:00
如果你要写一个方法集合的话,应该用第一种写法,第二种写法是错的。
|
13
makelove OP @xarthur 应该不是这个问题,这个很明显是对象。
你看我最后回复给 gynantim 里,那里没有对象了是个数字,结果也是 unknown |
15
hronro 2021-10-17 14:28:36 +08:00
@makelove #10
所以我才建议在 issue 列表里搜不到就直接去开新 issue,如果之前真的有人提过但你没搜到,会有人帮你标 duplicated 的。我过去几年给 TS 提过几个 issue,确实有一半是 duplicated 的,不过另外一半是确实是 TS 本身的问题 |
16
xarthur 2021-10-17 14:34:00 +08:00
@makelove 我现在倾向于 TS 出于某种可能会出现递归类型推断计算的可能性,直接暂停了这部分的类型推断,所以出现了 unknown 。
也或许这个是 TS 的 bug |
17
mx8Y3o5w3M70LC4y 2021-10-17 14:34:53 +08:00 via Android
@xarthur 第二个例子,你 b 在 declare 的时候返回值是 void,但是你调用的时候 b 的返回值类型是{},这代表一个空对象,并不是 void 。所以 ts 这时候不能推断出 s 是什么类型。。。
|
18
mx8Y3o5w3M70LC4y 2021-10-17 14:35:24 +08:00 via Android
|
19
makelove OP @lvdb 不是,js 语法这里的空{}不是返回空对象而是不返回东西,实际返回就是 void,或者你在里面写 return undefined as void 也是一个效果
|
20
mx8Y3o5w3M70LC4y 2021-10-17 14:58:13 +08:00 via Android
@makelove 建议你在 playground 里 console.log(typeof {}),看一下 log
|
21
makelove OP @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) => {}) |
22
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 |
23
FrankFang128 2021-10-17 15:44:13 +08:00 10
因为 () => 'ok' 没有 this,所以类型是明确的;
但 () { return 'ok' } 有 this,你没有指定 this 的类型,所以类型是不明确的; TS 的推测规则是,只要有任何一点不明确,就不推测。 所以你需改成 return { result(this:any) { return 'ok' } } 就行了 |
24
FrankFang128 2021-10-17 15:44:44 +08:00
|
25
FrankFang128 2021-10-17 15:45:34 +08:00
所以说 JS 的 this 是真的难
|
26
makelove OP @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 影响了啥 |
27
makelove OP @FrankFang128 你看后面的例子用数字了没有 this,我不知道这二个是不是同一个问题
|
28
FrankFang128 2021-10-17 15:53:27 +08:00
@makelove 原理看 23 楼:TS 的推测规则是,只要有任何一点不明确,就不推测。
你需要给 data 一个类型,就算是 any 都行 |
29
FrankFang128 2021-10-17 15:54:27 +08:00
总之就一句话:TS 的推测规则是,只要有任何一点不明确,就不推测。
|
30
cxk0 2021-10-17 19:46:39 +08:00
所以 V2 都是前端大佬,我 Java 后端在这里格格不入
|
31
leafre 2021-10-17 20:35:15 +08:00
s1 s2 返回类型 不一样
|
32
zbinlin 2021-10-18 00:41:18 +08:00
你这里 a 返回值跟 b 里的参数都需要推断,如果没有在 a 返回值或 b 的参数中推断出一个明确的类型,就无法确定类型。
你可以在 a 里定义下 this 或者将 b 里的 a 参数明确定义成出来(可以是 any 或 unknown )。 同理,你在楼层里举的例子也是一样的问题。 |
33
coolmenu 2021-10-18 07:12:10 +08:00
看的脑仁疼,ts 这么复杂吗?
|
34
sunwang 2021-10-18 09:27:45 +08:00
不知道楼主的 ts 是什么版本,我的 ts 是可以推断的,版本是 3.7.2
|
35
lscho 2021-10-18 10:09:45 +08:00
这明显 s 和 s2 不一样啊,楼主为什么会得出 s 和 s2 定义是几乎一样。匿名函数没有 this 。
|
36
makelove OP |
38
SmiteChow 2021-10-18 11:30:10 +08:00
讲句实话,写 ts 大部分人遇到复杂对象类型注解都写 any,实践里也没毛病。
|
39
thtznet 2021-10-18 11:43:56 +08:00
我不写 TS,只会写 JS 和 C#,传问 TS 是像 C#一样的 JS,但是看了这个问题,我表示 TS 在正宗的强类型(C#)前还是像个玩具语言,我本来打算学一下 TS 让 JS 写得舒服一点,现在有点慌,到底要不要把 JS 过渡到 TS ?既不像 C#那么严谨也丢失了 JS 的灵活和随性,这个搞得有点尴尬。
|
40
lscho 2021-10-18 11:51:23 +08:00
@makelove 我理解的和 23 楼一样。ts 是可以从上下文环境中推断类型的。() => 'ok' 这样本身没有 this,所以从外层上下文环境中可以推断。() { return 'ok' }这样单独生成了作用域,有了 this,且没有指定,所以类型不明确。
|
41
makelove OP @thtznet C#我只会一点,java 以前写过二年,但 TS 的类型能力全面吊打 java 和 c#是没有疑问的,大量 java/c#搞不定的类型设计 ts 就可以,可能是因为 js 的灵活性所以 ts 也只能这么灵活。平时碰到疑难杂证的可能还是很少的,js 社区这么快全面拥抱 ts 不是没有道理的,编程效率提高太多了
|
45
zbinlin 2021-10-19 10:03:25 +08:00
|
46
conateri 2022-09-01 22:20:07 +08:00 1
挖
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) => {} }) [playground]( https://www.typescriptlang.org/play?ts=4.7.4#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzDigxAB4BlAGngFlqBBAPgApREAueAbwCh55gxKJ3J94w+KyEiAlPAC8jWmIBGnZgFtONOYvgA3HFmA8AvjO08eYPAGcM8WwoJESzXv0EYJABkpiJKW9dJQBGHz9VdQ0Q7lMzGSA) 4.7.4 版本可以把 s 正确推断为 number, 是 bug 没错了 |