TypeScriptは静的型付け言語なのかという話
この辺の話。
静的型付言語は型システムを入れる事で変数への代入や関数適用を禁止する事だと思っているので、any を介して定義とは異なる物を渡せてしまう動作を許してしまっているのに静的と表現するのがモヤモヤする、という意味です。
— mattn (@mattn_jp) August 10, 2022
— mattn (@mattn_jp) August 10, 2022
(中略)動的/静的な型付けと強い/弱い型付けはそれぞれ違う概念な気が。 https://t.co/OhpkXV6U1s
— odz (@odz) August 11, 2022
静的型付の定義は型システムにより型付きの関数引数や変数へ異なる型の値が適用される際にはエラーになる(または警告される)というのが僕の認識なのです。
— mattn (@mattn_jp) August 11, 2022
これ、私はtest
を呼び出す際にany
からの暗黙のキャストが入っているという理解なのですよね。
こういうイメージ。
const test = (f: string) => console.log(typeof f); const foo: any = [1]; test(foo as string);
こういう互換性のないキャストがエラーもなくできてしまうという意味で弱い型付けなのではないかと。
分かるんですけど、TypeScriptでany経由すると型チェックが台無しになるのと、C/C++でvoid *やreinterpret_cast経由で型チェック台無しになるのにさしたる違いは無いような気がします。
— odz (@odz) August 11, 2022
最後書いているように、若干極論ではありますが、C/C++でポインタだとキャストしてしまえばなんでも渡せてしまうよね、という話はありますしね。
#include <iostream> #include <typeinfo> #include <cstdlib> #include <cxxabi.h> static char* buffer = 0; static size_t bufferSize = 0; static const std::string getTypeName(const std::type_info& type) { int status; char* name = abi::__cxa_demangle(type.name(), buffer, &bufferSize, &status); if (name && status == 0) { std::string s(name); buffer = name; return s; } return ""; } __attribute__((destructor)) static void cleanup() { if (buffer) { free(buffer); } } class Foo { public: virtual void method() {} }; class Bar : public Foo {}; class Baz { public: virtual void method() {} }; void test(const Foo* p) { std::cout << getTypeName(typeid(*p)) << std::endl; } int main() { Foo foo; Bar bar; Baz baz; test(&foo); // Foo test(&bar); // Bar test(reinterpret_cast<Foo *>(&baz)); // Baz return 0; }
まぁ、暗黙的にany
からキャストされてしまうTypeScriptと明示的なキャストが必要なC/C++を同列に語るのも乱暴といえば乱暴ではあります。
ただ、いずれにしても他の静的型付け言語で備えているような機能を一部備えていないとしても、TypeScriptの型システムのほとんどは静的なチェックなので、静的型付けと呼ぶしかないような気がします。
C++のdynamic_castやJavaでのダウンキャストは動的な型チェックでもあったりするので、そもそも無理に分類する必要もないんじゃないかという話もありますけど。