TypeScript(Udemy)
code: TypeScript
abstract class Person {
static speaces = 'Homo sapiens';
abstract explainJob(): void;
}
extends class Teather {
explainJob(){
console.log(~~~~);
}
}
abstractを入れることで継承先のクラスで必ず使わなくてはいけない。
abstractはインスタンスを生成できない
code: Typescript
abstract class Person {
// static getInstance()からアクセスするためにstaticにする
private static instance: Teacher;
}
extends class Teather {
explainJob(){
console.log(~~~~);
}
// シングルトン対応
private constructor(){
}
static getInstance(){
Teacher.instance = new Teacher(~~~~);
return Teacher.instance;
}
}
const teacher = Teather.getInstance();
private constructor()とるすことでシングルトンパターンに対応する
→一つのクラスから1つのインスタンスしか出さないようにする
→new で外部にインスタンスを作ることはできない
→staticメソッド内でnewする
interfaceはオブジェクトのみの型の変数
code:TypeScript
interface Human {
readonly name: string; // このreadonlyはimprementsで使ったclassには影響を与えない
age: number;
greeting(message: string): void;
}
const human: Human = {
name: 'quii',
age: 45,
console.log(message: string): void {
console.log(message);
}
}
class Developer implements Human {
constructor(public name: string, public age: number){}
greeting(~~){
~~~
}
}
const tmpDeveloper = {
name: 'aaaa',
age: 40,
experience: 2,
greeting(message: string){
console.log(message);
}
}
// 左辺のHumanは3つしか値を持っていないが、右側は4つもっている。これでもエラーはおきない
const user: Human = tmpDeveloper
inplementsを付けることによってclassとinterfaceを同時に使うことができる
typeの場合もimplementsを使える
interfaceの継承
code: TypeScript
interface Human extends Namable{
age: number;
greeting(message: string): void;
}
interface Namable {
name: string;
}
関数のinterface
code: TypeScript
interface addFunction {
(num1: number, num2: number): number;
}
無名関数のように書く
undefinedをunion型で指定するのと?を使うのは、一見同じように思えますが少し違います。以下のようにPerson型のageをnumber | undefinedで指定すると、Person型には必ずageを入れる必要がでてきます。こちらから確かめてみてください。
code: TypeScript
interface Person {
name: string;
age: number | undefined;
}
// ageが無いとエラーが出る
let person: Person = {
name: 'Peter',
};
greeting(message?: string){}
greeting(message: string = undefind){}
// この二つは同じ意味
つまり、age?: numberとすると、ageがなくてもエラーにはなりませんが、age: number | undefinedとすると、ageに明示的にundefinedか数値を入れる必要があります
二つの型を持つ型
code: TypeScript
type Engineer = {
name: string;
role: string;
}
type Blogger = {
name: string;
follower: number;
}
type EngineerBlog = Engineer & Blogger
// interfaceで定義する方法
interface Engineer {
name: string;
role: string;
}
interface Blogger {
name: string;
role: string;
}
interface EngineerBlog extends Engineer, Blogger {}
Typeguard
code: TypeScript
// typeofで型を調べることができる
function toUpperCase(x: string | number){
if(typeof x === 'string'){
x.trim;
}
}
// inを使うことで型内の特定の要素を判別できる
function describeProfile(nomadWorker: NomadWorker){
console.log(nomadWorker.name)
if('role' in nomadWorker){ // nomadWorkerにroleというkeyが存在するか?
console.log(nomadWorker.role)
}
}
class Dog {
speak(){
console.log('bow-wow')
}
}
class Bird {
speak(){
console.log('tweet-tweet')
}
fly(){
console.log('flutter')
}
}
// classの場合要素を調べるときにinstanceofを使う
type Pet = Dog | Bird;
function havePet(pet: Pet){
pet.speak();
if(pet instanceof Bird){ // Birdのインスタンスはpetですか?
pet.fly();
}
}
型アサーション
code: TypeScript
// 型アサーション(無理矢理型を変更する)
const input = <HTMLInputElement>document.getElementById('input');
const input = document.getElementById('input') as HTMLInputElement; // こっちを使う方がいいかも
input.value = 'initial input ~~~';
インデックスシグネチャ
code: TypeScript
interface Designer{
name: string;
}
const designer: Designer = {
name: 'Wiliam', // 自由にかける
role: 'uhee'
}
designer.role
関数のオーバーロード
code: TypeScript
function toUpperCase(x: string): string; // 関数の上にもう一つ引数と戻り値の型を明確にしてある関数を定義する
function toUpperCase(x: string | number){
if(typeof x === 'string'){
return x.toUpperCase();
}
return x;
}
const upperHello = toUpperCase('hello'); // パラメータに文字列が入っているなら型がstringになる
オプショナルチェーンニング
code: TypeScript
interface DownloadedData {
id: number,
user?: { // あってもなくても良い
name?: {
first: string;
last: string;
}
}
}
const downloadedData: DownloadedData = {
id: 1
}
console.log(downloadedData.user?.name?.last) // downloadedDataにuserが無いならUndefindを返す、有ればnameを返す
const userData = downloadedData.user ?? 'no-user' // ??を入れることで左のuserが無ければ'no-user'を返す
code: TypeScript
関数型のインターセクション
code: TypeScript
interface FuncA {
(a: number, b: string): number;
(a: string, b: number): number;
}
interface FuncB {
(a: number): number;
}
let intersectionFunc: FuncA & FuncB // intersectionFuncの型は上から順番にオーバーロードされた型になる
この場合、(a: number, b: string): number;になる
code: TypeScript
function advancedFn(...args: number[]){} // レストパラメータ、何個も引数に入れられる、型は配列で書く
advancedFn(0, 3, 4, 5)
// タプル内でレストパラメータを書ける
function advancedFn(...args: [number, string, boolean, ...number[]]){}
code: TypeScript
const peter = {
name: 'Peater',
age: 34
} as const;
type PeterType = typeof peter; // peterの型がPeterTypeに代入される
ジェネリクス
code: TypeScript
function copy<T>(value: T): T{ // 型を引数として渡してあげる。定義したTはどこでも使える
return value;
}
console.log(copy<string>('Tiika'))
console.log(copy('Tiika')) // <string>が無くても良い
function copy<T extends { name: string }>(value: T): T{ // extendsで引数として渡す型の制約を決める事ができる
return value;
}
console.log(copy({ name: 'Tikka'}))
keyof
code: TypeScript
type k = keyof { name: string; age: number } // オブジェクトのkeyを取り出してユニオン型に結合し代入する
function copy<T extends { name: string }, U extends keyof T>(value: T, key: U): T{
return value;
}
console.log(copy({ name: 'Tikka', age: 39}, 'age'))
Tのkeyのname、ageのユニオン型
クラスにジェネリクスを使う
code: TypeScript
class LighetDatabase<T extends string | number>{
private data: T[] = [];
add(item: T){
this.data.push(item)
}
remove(item: T){
this.data.splice(this.data.indexOf(item), 1) // itemの番号を一つ削除する
}
}
const stringlightData = new LighetDatabase<string>()
stringlightData.add('Apple');
stringlightData.add('Banana');
stringlightData.remove('Banana');
Utility型
code: TypeScript
Array<string> // ジェネリクス型<型の引数>
type Todoable = Partial<Todo> // Utility型(型のライブラリ)のひとつ、全てオプショナルパラメータになる
type Readable = Readonly<Todo> // Utility型(型のライブラリ)のひとつ、全てリードオンリーになる
const fetchData: Promise<string> = new Promise(resolve => { // Promiseは<Unkonwn>だが<string>を指定できる
setTimeout(() => {
resolve('hello');
})
})
fetchData.then(data => {
data.toUpperCase();
})
MappedTypes(型のfor文)
code: TypeScript
interface Todo {
title: string;
text: string;
}
type MappedTypes = {
}
型のif文
code: TypeScript
type ConditionalTypes = 'tomato' extends string ? number : boolean // 'tomato'がstringに入るか?入るならnumber型になる、入らないならboolean
デコレーター
code: TypeScript
function Logging(constructor: Function){ // クラスの型(Functionは関数の一般の型)
console.log('Logging…');
}
@Logging // クラス全体にデコレーションしている、デコレーションはクラスの定義時に実行される、インスタンスを生成しなくてもいい
class User {
name = 'TIkka';
constructor(){
console.log('User was created!');
}
}
function Logging(message: string){ // デコレーターを返す関数。デコレーターファクトリー
return function (constructor: Function){
console.log('Logging…');
}
}
@Logging('Hello') // ()を付けて引数を受け取ることもできる
class User {
name = 'TIkka';
constructor(){
console.log('User was created!');
}
}
デコレーターでHtmlを挿入
code: TypeScript
function Component(template: string, selector: string){
return function(constructor: { new(...args: any[]): { name: string }}) { // 型をオブジェクトにし、keyをnew(), 値を受け取りたいオブジェクトにすることでnewできる関数であることを示す
const mountedElement = document.querySelector(selector);
const instance = new constructor(); // インスタンスを生成する
if(mountedElement){
mountedElement.innerHTML = template;
mountedElement.querySelector('h1')!.textContent = instance.name;
}
}
}
@Component('<h1>{{ name }}</h1>', '#app') // Userクラスのnameと<h1>のnameが対応し、idがappのタグに表示されるデコレーター
@Logging('Hello') // クラス全体にデコレーションしている、デコレーションはクラスの定義時に実行される、インスタンスを生成しなくてもいい
class User {
name = 'TIkka';
constructor(){
console.log('User was created!');
}
}
デコレーターは下から上に実行される
プロパティデコレータ
code: TypeScript
function PropertyLogging(target: any, propertyKey: string){ //プロパティデコレーターは引数を二つとる、第一引数はクラスのプロトタイプをとる
console.log(target);
console.log(propertyKey);
}
@Component('<h1>{{ name }}</h1>', '#app')
@Logging('Hello')
class User {
@PropertyLogging // クラスデコレータよりもプロパティデコレーターのほうが先に実行される
name = 'TIkka';
constructor(public age: number){
console.log('User was created!');
}
}
Javascriptでは関数はオブジェクト扱い
Javascriptではオブジェクトの中身を探して、プロパティやメソッドが無い場合_proto_の内部を探す
_prototype_はUser.prototypeが生成する
メソッドデコレーター
code: TypeScript
function MethodLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor){ // PropertyDescriptorはオブジェクト,メタデータを取得する,PropertyDescriptorを返す
console.log(MethodLogging)
}
@Component('<h1>{{ name }}</h1>', '#app')
@Logging('Hello')
class User {
name = 'TIkka';
constructor(public age: number){
console.log('User was created!');
}
@MethodLogging
}
const user = {name: 'Tikka', age: 40}
object.getOwnpropertydescriptor(user, 'name');→ブラウザが裏側で持っている情報を表示
{
configurable: true
enumerable: false
value:
writable: true
}
アクセサデコレータ
code:TypeScript
function AccessorLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor){ // アクセサ―デコレータはPropertyDescriptorを返す
console.log(MethodLogging)
}
class User {
name = 'TIkka';
constructor(public _age: number){
console.log('User was created!');
}
@AccessorLogging
get age(){
return _age;
}
set age(value){
this._age = value;
}
@enumerable(false)
}